Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquicktextinput.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7#include "qquickwindow.h"
8
9#include <private/qqmlglobal_p.h>
10#include <private/qv4scopedvalue_p.h>
11
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/qmimedata.h>
14#include <QtQml/qqmlinfo.h>
15#include <QtGui/qevent.h>
16#include <QTextBoundaryFinder>
18#include <QtQuick/qsgsimplerectnode.h>
19
20#include <QtGui/qstylehints.h>
21#include <QtGui/qinputmethod.h>
22#include <QtCore/qmath.h>
23
24#if QT_CONFIG(accessibility)
25#include "qaccessible.h"
26#include "qquickaccessibleattached_p.h"
27#endif
28
29#include <QtGui/private/qtextengine_p.h>
30#include <QtGui/private/qinputcontrol_p.h>
31
33
34DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
35Q_STATIC_LOGGING_CATEGORY(lcQuickTextInput, "qt.quick.textInput")
36
37/*!
38 \qmltype TextInput
39 \nativetype QQuickTextInput
40 \inqmlmodule QtQuick
41 \ingroup qtquick-visual
42 \ingroup qtquick-input
43 \inherits Item
44 \brief Displays an editable line of text.
45
46 The TextInput type displays a single line of editable plain text.
47
48 TextInput is used to accept a line of text input. Input constraints
49 can be placed on a TextInput item (for example, through a \l validator or \l inputMask),
50 and setting \l echoMode to an appropriate value enables TextInput to be used for
51 a password input field.
52
53 To react to the user accepting text via the Return or Enter keys, handle
54 the \l accepted() signal. When Return or Enter are pressed and the text
55 input loses focus, \l editingFinished() will be emitted. When the text is
56 edited in any way by the user, \l textEdited() is emitted. These signals
57 \l {Property change signals}{should be preferred} over \c textChanged()
58 in most cases.
59
60 On \macos, the Up/Down key bindings for Home/End are explicitly disabled.
61 If you want such bindings (on any platform), you will need to construct them in QML.
62
63 \sa TextEdit, Text
64*/
65QQuickTextInput::QQuickTextInput(QQuickItem* parent)
66: QQuickImplicitSizeItem(*(new QQuickTextInputPrivate), parent)
67{
68 Q_D(QQuickTextInput);
69 d->init();
70}
71
72QQuickTextInput::QQuickTextInput(QQuickTextInputPrivate &dd, QQuickItem *parent)
73: QQuickImplicitSizeItem(dd, parent)
74{
75 Q_D(QQuickTextInput);
76 d->init();
77}
78
79QQuickTextInput::~QQuickTextInput()
80{
81}
82
83void QQuickTextInput::componentComplete()
84{
85 Q_D(QQuickTextInput);
86
87 QQuickImplicitSizeItem::componentComplete();
88
89 d->checkIsValid();
90 d->updateLayout();
91 updateCursorRectangle();
92 if (d->cursorComponent && isCursorVisible())
93 QQuickTextUtil::createCursor(d);
94#if QT_CONFIG(accessibility)
95 if (QAccessible::isActive())
96 d->accessibilityActiveChanged(true);
97#endif
98}
99
100/*!
101 \qmlproperty string QtQuick::TextInput::text
102
103 The text in the TextInput.
104
105 Note that some keyboards use a predictive function. In this case,
106 the text being composed by the input method is not part of this property.
107 The part of the text related to the predictions is underlined and stored in
108 the \l preeditText property. To get whole text displayed in the TextInput
109 use \l displayText property.
110
111 \sa clear(), displayText, preeditText, accepted(), editingFinished(),
112 textEdited()
113*/
114QString QQuickTextInput::text() const
115{
116 Q_D(const QQuickTextInput);
117
118 QString content = d->m_text;
119 QString res = d->m_maskData ? d->stripString(content) : content;
120 return (res.isNull() ? QString::fromLatin1("") : res);
121}
122
123void QQuickTextInput::invalidate()
124{
125 Q_D(QQuickTextInput);
126 d->updateLayout();
127 invalidateFontCaches();
128}
129
130void QQuickTextInput::setText(const QString &s)
131{
132 Q_D(QQuickTextInput);
133 if (s == text())
134 return;
135
136#if QT_CONFIG(im)
137 d->cancelPreedit();
138#endif
139 d->internalSetText(s, -1, false);
140}
141
142
143/*!
144 \qmlproperty enumeration QtQuick::TextInput::renderType
145
146 Override the default rendering type for this component.
147
148 Supported render types are:
149
150 \value TextInput.QtRendering Text is rendered using a scalable distance field for each glyph.
151 \value TextInput.NativeRendering Text is rendered using a platform-specific technique.
152 \value TextInput.CurveRendering Text is rendered using a curve rasterizer running directly on
153 the graphics hardware. (Introduced in Qt 6.7.0.)
154
155 Select \c TextInput.NativeRendering if you prefer text to look native on the target platform and do
156 not require advanced features such as transformation of the text. Using such features in
157 combination with the NativeRendering render type will lend poor and sometimes pixelated
158 results.
159
160 Both \c TextInput.QtRendering and \c TextInput.CurveRendering are hardware-accelerated techniques.
161 \c QtRendering is the faster of the two, but uses more memory and will exhibit rendering
162 artifacts at large sizes. \c CurveRendering should be considered as an alternative in cases
163 where \c QtRendering does not give good visual results or where reducing graphics memory
164 consumption is a priority.
165
166 The default rendering type is determined by \l QQuickWindow::textRenderType().
167*/
168QQuickTextInput::RenderType QQuickTextInput::renderType() const
169{
170 Q_D(const QQuickTextInput);
171 return d->renderType;
172}
173
174void QQuickTextInput::setRenderType(QQuickTextInput::RenderType renderType)
175{
176 Q_D(QQuickTextInput);
177 if (d->renderType == renderType)
178 return;
179
180 d->renderType = renderType;
181 emit renderTypeChanged();
182
183 if (isComponentComplete())
184 d->updateLayout();
185}
186
187/*!
188 \qmlproperty int QtQuick::TextInput::length
189
190 Returns the total number of characters in the TextInput item.
191
192 If the TextInput has an inputMask the length will include mask characters and may differ
193 from the length of the string returned by the \l text property.
194
195 This property can be faster than querying the length the \l text property as it doesn't
196 require any copying or conversion of the TextInput's internal string data.
197*/
198
199int QQuickTextInput::length() const
200{
201 Q_D(const QQuickTextInput);
202 return d->m_text.size();
203}
204
205/*!
206 \qmlmethod string QtQuick::TextInput::getText(int start, int end)
207
208 Returns the section of text that is between the \a start and \a end positions.
209
210 If the TextInput has an inputMask the length will include mask characters.
211*/
212
213QString QQuickTextInput::getText(int start, int end) const
214{
215 Q_D(const QQuickTextInput);
216
217 if (start > end)
218 qSwap(start, end);
219
220 return d->m_text.mid(start, end - start);
221}
222
223QString QQuickTextInputPrivate::realText() const
224{
225 QString res = m_maskData ? stripString(m_text) : m_text;
226 return (res.isNull() ? QString::fromLatin1("") : res);
227}
228
229/*!
230 \qmlproperty string QtQuick::TextInput::font.family
231
232 Sets the family name of the font.
233
234 \include qmltypereference.qdoc qml-font-family
235*/
236
237/*!
238 \qmlproperty string QtQuick::TextInput::font.styleName
239 \since 5.6
240
241 Sets the style name of the font.
242
243 The style name is case insensitive. If set, the font will be matched against style name instead
244 of the font properties \l font.weight, \l font.bold and \l font.italic.
245*/
246
247/*!
248 \qmlproperty bool QtQuick::TextInput::font.bold
249
250 Sets whether the font weight is bold.
251*/
252
253/*!
254 \qmlproperty int QtQuick::TextInput::font.weight
255
256 \include qmltypereference.qdoc qml-font-weight
257*/
258
259/*!
260 \qmlproperty bool QtQuick::TextInput::font.italic
261
262 Sets whether the font has an italic style.
263*/
264
265/*!
266 \qmlproperty bool QtQuick::TextInput::font.underline
267
268 Sets whether the text is underlined.
269*/
270
271/*!
272 \qmlproperty bool QtQuick::TextInput::font.strikeout
273
274 Sets whether the font has a strikeout style.
275*/
276
277/*!
278 \qmlproperty real QtQuick::TextInput::font.pointSize
279
280 Sets the font size in points. The point size must be greater than zero.
281*/
282
283/*!
284 \qmlproperty int QtQuick::TextInput::font.pixelSize
285
286 Sets the font size in pixels.
287
288 Using this function makes the font device dependent.
289 Use \c pointSize to set the size of the font in a device independent manner.
290*/
291
292/*!
293 \qmlproperty real QtQuick::TextInput::font.letterSpacing
294
295 Sets the letter spacing for the font.
296
297 \include qmltypereference.qdoc qml-font-letter-spacing
298*/
299
300/*!
301 \qmlproperty real QtQuick::TextInput::font.wordSpacing
302
303 Sets the word spacing for the font.
304
305 \include qmltypereference.qdoc qml-font-word-spacing
306*/
307
308/*!
309 \qmlproperty enumeration QtQuick::TextInput::font.capitalization
310
311 Sets the capitalization for the text.
312
313 \include qmltypereference.qdoc qml-font-capitalization
314*/
315
316/*!
317 \qmlproperty enumeration QtQuick::TextInput::font.hintingPreference
318 \since 5.8
319
320 Sets the preferred hinting on the text.
321
322 \include qmltypereference.qdoc qml-font-hinting-preference
323*/
324
325/*!
326 \qmlproperty bool QtQuick::TextInput::font.kerning
327 \since 5.10
328
329 \include qmltypereference.qdoc qml-font-kerning
330*/
331
332/*!
333 \qmlproperty bool QtQuick::TextInput::font.preferShaping
334 \since 5.10
335
336 \include qmltypereference.qdoc qml-font-prefer-shaping
337*/
338
339/*!
340 \qmlproperty object QtQuick::TextInput::font.variableAxes
341 \since 6.7
342
343 \include qmltypereference.qdoc qml-font-variable-axes
344*/
345
346/*!
347 \qmlproperty object QtQuick::TextInput::font.features
348 \since 6.6
349
350 \include qmltypereference.qdoc qml-font-features
351*/
352
353/*!
354 \qmlproperty bool QtQuick::TextInput::font.contextFontMerging
355 \since 6.8
356
357 \include qmltypereference.qdoc qml-font-context-font-merging
358*/
359
360/*!
361 \qmlproperty bool QtQuick::TextInput::font.preferTypoLineMetrics
362 \since 6.8
363
364 \include qmltypereference.qdoc qml-font-prefer-typo-line-metrics
365*/
366QFont QQuickTextInput::font() const
367{
368 Q_D(const QQuickTextInput);
369 return d->sourceFont;
370}
371
372void QQuickTextInput::setFont(const QFont &font)
373{
374 Q_D(QQuickTextInput);
375 if (d->sourceFont == font)
376 return;
377
378 d->sourceFont = font;
379 QFont oldFont = d->font;
380 d->font = font;
381 if (d->font.pointSizeF() != -1) {
382 // 0.5pt resolution
383 qreal size = qRound(d->font.pointSizeF()*2.0);
384 d->font.setPointSizeF(size/2.0);
385 }
386 if (oldFont != d->font) {
387 d->updateLayout();
388 updateCursorRectangle();
389#if QT_CONFIG(im)
390 updateInputMethod(Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle);
391#endif
392 }
393 emit fontChanged(d->sourceFont);
394}
395
396/*!
397 \qmlproperty color QtQuick::TextInput::color
398
399 The text color.
400*/
401QColor QQuickTextInput::color() const
402{
403 Q_D(const QQuickTextInput);
404 return d->color;
405}
406
407void QQuickTextInput::setColor(const QColor &c)
408{
409 Q_D(QQuickTextInput);
410 if (c != d->color) {
411 d->color = c;
412 d->textLayoutDirty = true;
413 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
414 polish();
415 update();
416 emit colorChanged();
417 }
418}
419
420
421/*!
422 \qmlproperty color QtQuick::TextInput::selectionColor
423
424 The text highlight color, used behind selections.
425*/
426QColor QQuickTextInput::selectionColor() const
427{
428 Q_D(const QQuickTextInput);
429 return d->selectionColor;
430}
431
432void QQuickTextInput::setSelectionColor(const QColor &color)
433{
434 Q_D(QQuickTextInput);
435 if (d->selectionColor == color)
436 return;
437
438 d->selectionColor = color;
439 if (d->hasSelectedText()) {
440 d->textLayoutDirty = true;
441 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
442 polish();
443 update();
444 }
445 emit selectionColorChanged();
446}
447/*!
448 \qmlproperty color QtQuick::TextInput::selectedTextColor
449
450 The highlighted text color, used in selections.
451*/
452QColor QQuickTextInput::selectedTextColor() const
453{
454 Q_D(const QQuickTextInput);
455 return d->selectedTextColor;
456}
457
458void QQuickTextInput::setSelectedTextColor(const QColor &color)
459{
460 Q_D(QQuickTextInput);
461 if (d->selectedTextColor == color)
462 return;
463
464 d->selectedTextColor = color;
465 if (d->hasSelectedText()) {
466 d->textLayoutDirty = true;
467 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
468 polish();
469 update();
470 }
471 emit selectedTextColorChanged();
472}
473
474/*!
475 \qmlproperty enumeration QtQuick::TextInput::effectiveHorizontalAlignment
476 \readonly
477
478 When using the attached property LayoutMirroring::enabled to mirror application
479 layouts, the horizontal alignment of text will also be mirrored. However, the property
480 \l horizontalAlignment will remain unchanged. To query the effective horizontal alignment
481 of TextInput, use the read-only property \c effectiveHorizontalAlignment.
482*/
483/*!
484 \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment
485 \qmlproperty enumeration QtQuick::TextInput::verticalAlignment
486
487 Sets the horizontal alignment of the text within the TextInput item's
488 width and height. By default, the text alignment follows the natural alignment
489 of the text, for example text that is read from left to right will be aligned to
490 the left.
491
492 TextInput does not have vertical alignment, as the natural height is
493 exactly the height of the single line of text. If you set the height
494 manually to something larger, TextInput will always be top aligned
495 vertically. You can use anchors to align it however you want within
496 another item.
497
498 The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and
499 \c TextInput.AlignHCenter.
500
501 Valid values for \c verticalAlignment are \c TextInput.AlignTop (default),
502 \c TextInput.AlignBottom \c TextInput.AlignVCenter.
503
504 When using the attached property LayoutMirroring::enabled to mirror application
505 layouts, the horizontal alignment of text will also be mirrored. However, the property
506 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
507 of TextInput, use the read-only property \l effectiveHorizontalAlignment.
508*/
509QQuickTextInput::HAlignment QQuickTextInput::hAlign() const
510{
511 Q_D(const QQuickTextInput);
512 return d->hAlign;
513}
514
515void QQuickTextInput::setHAlign(HAlignment align)
516{
517 Q_D(QQuickTextInput);
518
519 if (d->setHAlign(align, true) && isComponentComplete()) {
520 d->updateLayout();
521 updateCursorRectangle();
522 }
523}
524
525void QQuickTextInput::resetHAlign()
526{
527 Q_D(QQuickTextInput);
528 d->hAlignImplicit = true;
529 if (d->determineHorizontalAlignment() && isComponentComplete()) {
530 d->updateLayout();
531 updateCursorRectangle();
532 }
533}
534
535QQuickTextInput::HAlignment QQuickTextInput::effectiveHAlign() const
536{
537 Q_D(const QQuickTextInput);
538 QQuickTextInput::HAlignment effectiveAlignment = d->hAlign;
539 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
540 switch (d->hAlign) {
541 case QQuickTextInput::AlignLeft:
542 effectiveAlignment = QQuickTextInput::AlignRight;
543 break;
544 case QQuickTextInput::AlignRight:
545 effectiveAlignment = QQuickTextInput::AlignLeft;
546 break;
547 default:
548 break;
549 }
550 }
551 return effectiveAlignment;
552}
553
554bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment align, bool forceAlign)
555{
556 Q_Q(QQuickTextInput);
557 if (align > QQuickTextInput::AlignHCenter)
558 return false; // justify is not supported
559
560 if (hAlign == align && !forceAlign)
561 return false;
562
563 const bool wasImplicit = hAlignImplicit;
564 const auto oldEffectiveHAlign = q->effectiveHAlign();
565
566 hAlignImplicit = !forceAlign;
567 if (hAlign != align) {
568 hAlign = align;
569 emit q->horizontalAlignmentChanged(align);
570 }
571
572 if (q->effectiveHAlign() != oldEffectiveHAlign) {
573 emit q->effectiveHorizontalAlignmentChanged();
574 return true;
575 }
576
577 if (forceAlign && wasImplicit) {
578 // QTBUG-120052 - when horizontal text alignment is set explicitly,
579 // we need notify any other controls that may depend on it, like QQuickPlaceholderText
580 emit q->effectiveHorizontalAlignmentChanged();
581 }
582 return false;
583}
584
585Qt::LayoutDirection QQuickTextInputPrivate::textDirection() const
586{
587 QString text = m_text;
588#if QT_CONFIG(im)
589 if (text.isEmpty())
590 text = m_textLayout.preeditAreaText();
591#endif
592
593 const QChar *character = text.constData();
594 while (!character->isNull()) {
595 switch (character->direction()) {
596 case QChar::DirL:
597 return Qt::LeftToRight;
598 case QChar::DirR:
599 case QChar::DirAL:
600 case QChar::DirAN:
601 return Qt::RightToLeft;
602 default:
603 break;
604 }
605 character++;
606 }
607 return Qt::LayoutDirectionAuto;
608}
609
610Qt::LayoutDirection QQuickTextInputPrivate::layoutDirection() const
611{
612 Qt::LayoutDirection direction = m_layoutDirection;
613 if (direction == Qt::LayoutDirectionAuto) {
614 direction = textDirection();
615#if QT_CONFIG(im)
616 if (direction == Qt::LayoutDirectionAuto)
617 direction = QGuiApplication::inputMethod()->inputDirection();
618#endif
619 }
620 return (direction == Qt::LayoutDirectionAuto) ? Qt::LeftToRight : direction;
621}
622
623bool QQuickTextInputPrivate::determineHorizontalAlignment()
624{
625 if (!hAlignImplicit)
626 return false;
627
628 // if no explicit alignment has been set, follow the natural layout direction of the text
629 Qt::LayoutDirection direction = textDirection();
630#if QT_CONFIG(im)
631 if (direction == Qt::LayoutDirectionAuto)
632 direction = QGuiApplication::inputMethod()->inputDirection();
633#endif
634
635 const auto implicitHAlign = direction == Qt::RightToLeft ?
636 QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft;
637 return setHAlign(implicitHAlign);
638}
639
640QQuickTextInput::VAlignment QQuickTextInput::vAlign() const
641{
642 Q_D(const QQuickTextInput);
643 return d->vAlign;
644}
645
646void QQuickTextInput::setVAlign(QQuickTextInput::VAlignment alignment)
647{
648 Q_D(QQuickTextInput);
649 if (alignment == d->vAlign)
650 return;
651 d->vAlign = alignment;
652 emit verticalAlignmentChanged(d->vAlign);
653 if (isComponentComplete()) {
654 updateCursorRectangle();
655 d->updateBaselineOffset();
656 }
657}
658
659/*!
660 \qmlproperty enumeration QtQuick::TextInput::wrapMode
661
662 Set this property to wrap the text to the TextInput item's width.
663 The text will only wrap if an explicit width has been set.
664
665 \value TextInput.NoWrap
666 (default) no wrapping will be performed. If the text contains
667 insufficient newlines, then \l contentWidth will exceed a set width.
668 \value TextInput.WordWrap
669 wrapping is done on word boundaries only. If a word is too long,
670 \l contentWidth will exceed a set width.
671 \value TextInput.WrapAnywhere
672 wrapping is done at any point on a line, even if it occurs in the middle of a word.
673 \value TextInput.Wrap
674 if possible, wrapping occurs at a word boundary; otherwise it will occur
675 at the appropriate point on the line, even in the middle of a word.
676
677 The default is TextInput.NoWrap. If you set a width, consider using TextInput.Wrap.
678*/
679QQuickTextInput::WrapMode QQuickTextInput::wrapMode() const
680{
681 Q_D(const QQuickTextInput);
682 return d->wrapMode;
683}
684
685void QQuickTextInput::setWrapMode(WrapMode mode)
686{
687 Q_D(QQuickTextInput);
688 if (mode == d->wrapMode)
689 return;
690 d->wrapMode = mode;
691 d->updateLayout();
692 updateCursorRectangle();
693 emit wrapModeChanged();
694}
695
696void QQuickTextInputPrivate::mirrorChange()
697{
698 Q_Q(QQuickTextInput);
699 if (q->isComponentComplete()) {
700 if (!hAlignImplicit && (hAlign == QQuickTextInput::AlignRight || hAlign == QQuickTextInput::AlignLeft)) {
701 q->updateCursorRectangle();
702 emit q->effectiveHorizontalAlignmentChanged();
703 }
704 }
705}
706
707/*!
708 \qmlproperty bool QtQuick::TextInput::readOnly
709
710 Sets whether user input can modify the contents of the TextInput.
711
712 If readOnly is set to true, then user input will not affect the text
713 property. Any bindings or attempts to set the text property will still
714 work.
715*/
716bool QQuickTextInput::isReadOnly() const
717{
718 Q_D(const QQuickTextInput);
719 return d->m_readOnly;
720}
721
722void QQuickTextInput::setReadOnly(bool ro)
723{
724 Q_D(QQuickTextInput);
725 if (d->m_readOnly == ro)
726 return;
727
728#if QT_CONFIG(im)
729 setFlag(QQuickItem::ItemAcceptsInputMethod, !ro);
730#endif
731 d->m_readOnly = ro;
732 d->setCursorPosition(d->end());
733#if QT_CONFIG(im)
734 updateInputMethod(Qt::ImEnabled);
735#endif
736 q_canPasteChanged();
737 d->emitUndoRedoChanged();
738 emit readOnlyChanged(ro);
739 if (ro) {
740 setCursorVisible(false);
741 } else if (hasActiveFocus()) {
742 setCursorVisible(true);
743 }
744 update();
745}
746
747/*!
748 \qmlproperty int QtQuick::TextInput::maximumLength
749 The maximum permitted length of the text in the TextInput.
750
751 If the text is too long, it is truncated at the limit.
752
753 By default, this property contains a value of 32767.
754*/
755int QQuickTextInput::maxLength() const
756{
757 Q_D(const QQuickTextInput);
758 return d->m_maxLength;
759}
760
761void QQuickTextInput::setMaxLength(int ml)
762{
763 Q_D(QQuickTextInput);
764 if (d->m_maxLength == ml || d->m_maskData)
765 return;
766
767 d->m_maxLength = ml;
768 d->internalSetText(d->m_text, -1, false);
769
770 emit maximumLengthChanged(ml);
771}
772
773/*!
774 \qmlproperty bool QtQuick::TextInput::cursorVisible
775 Set to true when the TextInput shows a cursor.
776
777 This property is set and unset when the TextInput gets active focus, so that other
778 properties can be bound to whether the cursor is currently showing. As it
779 gets set and unset automatically, when you set the value yourself you must
780 keep in mind that your value may be overwritten.
781
782 It can be set directly in script, for example if a KeyProxy might
783 forward keys to it and you desire it to look active when this happens
784 (but without actually giving it active focus).
785
786 It should not be set directly on the item, like in the below QML,
787 as the specified value will be overridden and lost on focus changes.
788
789 \code
790 TextInput {
791 text: "Text"
792 cursorVisible: false
793 }
794 \endcode
795
796 In the above snippet the cursor will still become visible when the
797 TextInput gains active focus.
798*/
799bool QQuickTextInput::isCursorVisible() const
800{
801 Q_D(const QQuickTextInput);
802 return d->cursorVisible;
803}
804
805void QQuickTextInput::setCursorVisible(bool on)
806{
807 Q_D(QQuickTextInput);
808 if (d->cursorVisible == on)
809 return;
810 d->cursorVisible = on;
811 if (on && isComponentComplete())
812 QQuickTextUtil::createCursor(d);
813 if (!d->cursorItem)
814 d->updateCursorBlinking();
815 emit cursorVisibleChanged(d->cursorVisible);
816}
817
818/*!
819 \qmlproperty int QtQuick::TextInput::cursorPosition
820 The position of the cursor in the TextInput. The cursor is positioned between
821 characters.
822
823 \note The \e characters in this case refer to the string of \l QChar objects,
824 therefore 16-bit Unicode characters, and the position is considered an index
825 into this string. This does not necessarily correspond to individual graphemes
826 in the writing system, as a single grapheme may be represented by multiple
827 Unicode characters, such as in the case of surrogate pairs, linguistic
828 ligatures or diacritics.
829
830 \l displayText is different if echoMode is set to \c {TextInput.Password}:
831 then each passwordCharacter is a "narrow" character (the cursorPosition always
832 moves by 1), even if the text in the TextInput is not.
833*/
834int QQuickTextInput::cursorPosition() const
835{
836 Q_D(const QQuickTextInput);
837 return d->m_cursor;
838}
839
840void QQuickTextInput::setCursorPosition(int cp)
841{
842 Q_D(QQuickTextInput);
843 if (cp < 0 || cp > text().size())
844 return;
845 d->moveCursor(cp);
846}
847
848/*!
849 \qmlproperty rectangle QtQuick::TextInput::cursorRectangle
850 \readonly
851
852 The rectangle where the standard text cursor is rendered within the text input. Read only.
853
854 The position and height of a custom cursorDelegate are updated to follow the cursorRectangle
855 automatically when it changes. The width of the delegate is unaffected by changes in the
856 cursor rectangle.
857*/
858
859QRectF QQuickTextInput::cursorRectangle() const
860{
861 Q_D(const QQuickTextInput);
862
863 int c = d->m_cursor;
864#if QT_CONFIG(im)
865 c += d->m_preeditCursor;
866#endif
867 if (d->m_echoMode == NoEcho)
868 c = 0;
869 QTextLine l = d->m_textLayout.lineForTextPosition(c);
870 if (!l.isValid())
871 return QRectF();
872 qreal x = l.cursorToX(c) - d->hscroll + leftPadding();
873 qreal y = l.y() - d->vscroll + topPadding();
874 qreal w = 1;
875 if (d->overwriteMode) {
876 if (c < text().size())
877 w = l.cursorToX(c + 1) - x;
878 else
879 w = QFontMetrics(font()).horizontalAdvance(QLatin1Char(' ')); // in sync with QTextLine::draw()
880 }
881 return QRectF(x, y, w, l.height());
882}
883
884/*!
885 \qmlproperty int QtQuick::TextInput::selectionStart
886
887 The cursor position before the first character in the current selection.
888
889 This property is read-only. To change the selection, use select(start,end),
890 selectAll(), or selectWord().
891
892 \readonly
893 \sa selectionEnd, cursorPosition, selectedText
894*/
895int QQuickTextInput::selectionStart() const
896{
897 Q_D(const QQuickTextInput);
898 return d->lastSelectionStart;
899}
900/*!
901 \qmlproperty int QtQuick::TextInput::selectionEnd
902
903 The cursor position after the last character in the current selection.
904
905 This property is read-only. To change the selection, use select(start,end),
906 selectAll(), or selectWord().
907
908 \readonly
909 \sa selectionStart, cursorPosition, selectedText
910*/
911int QQuickTextInput::selectionEnd() const
912{
913 Q_D(const QQuickTextInput);
914 return d->lastSelectionEnd;
915}
916/*!
917 \qmlmethod void QtQuick::TextInput::select(int start, int end)
918
919 Causes the text from \a start to \a end to be selected.
920
921 If either start or end is out of range, the selection is not changed.
922
923 After calling this, selectionStart will become the lesser
924 and selectionEnd will become the greater (regardless of the order passed
925 to this method).
926
927 \sa selectionStart, selectionEnd
928*/
929void QQuickTextInput::select(int start, int end)
930{
931 Q_D(QQuickTextInput);
932 if (start < 0 || end < 0 || start > d->m_text.size() || end > d->m_text.size())
933 return;
934 d->setSelection(start, end-start);
935}
936
937/*!
938 \qmlproperty string QtQuick::TextInput::selectedText
939 \readonly
940
941 This read-only property provides the text currently selected in the
942 text input.
943
944 It is equivalent to the following snippet, but is faster and easier
945 to use.
946
947 \qml
948 myTextInput.text.toString().substring(myTextInput.selectionStart,
949 myTextInput.selectionEnd);
950 \endqml
951*/
952QString QQuickTextInput::selectedText() const
953{
954 Q_D(const QQuickTextInput);
955 return d->selectedText();
956}
957
958/*!
959 \qmlproperty bool QtQuick::TextInput::activeFocusOnPress
960
961 Whether the TextInput should gain active focus on a mouse press. By default this is
962 set to true.
963*/
964bool QQuickTextInput::focusOnPress() const
965{
966 Q_D(const QQuickTextInput);
967 return d->focusOnPress;
968}
969
970void QQuickTextInput::setFocusOnPress(bool b)
971{
972 Q_D(QQuickTextInput);
973 if (d->focusOnPress == b)
974 return;
975
976 d->focusOnPress = b;
977
978 emit activeFocusOnPressChanged(d->focusOnPress);
979}
980/*!
981 \qmlproperty bool QtQuick::TextInput::autoScroll
982
983 Whether the TextInput should scroll when the text is longer than the width. By default this is
984 set to true.
985
986 \sa ensureVisible()
987*/
988bool QQuickTextInput::autoScroll() const
989{
990 Q_D(const QQuickTextInput);
991 return d->autoScroll;
992}
993
994void QQuickTextInput::setAutoScroll(bool b)
995{
996 Q_D(QQuickTextInput);
997 if (d->autoScroll == b)
998 return;
999
1000 d->autoScroll = b;
1001 //We need to repaint so that the scrolling is taking into account.
1002 updateCursorRectangle();
1003 emit autoScrollChanged(d->autoScroll);
1004}
1005
1006#if QT_CONFIG(validator)
1007/*!
1008 \qmlproperty Validator QtQuick::TextInput::validator
1009
1010 Allows you to set a validator on the TextInput. When a validator is set
1011 the TextInput will only accept input which leaves the text property in
1012 an acceptable or intermediate state. The accepted signal will only be sent
1013 if the text is in an acceptable state when enter is pressed.
1014
1015 Currently supported validators are IntValidator, DoubleValidator
1016 and RegularExpressionValidator. An example of using validators is shown
1017 below, which allows input of integers between 11 and 31 into the
1018 text input:
1019
1020 \code
1021 import QtQuick 2.0
1022 TextInput{
1023 validator: IntValidator{bottom: 11; top: 31;}
1024 focus: true
1025 }
1026 \endcode
1027
1028 \sa acceptableInput, inputMask
1029*/
1030
1031QValidator* QQuickTextInput::validator() const
1032{
1033 Q_D(const QQuickTextInput);
1034 return d->m_validator;
1035}
1036
1037void QQuickTextInput::setValidator(QValidator* v)
1038{
1039 Q_D(QQuickTextInput);
1040 if (d->m_validator == v)
1041 return;
1042
1043 if (d->m_validator) {
1044 qmlobject_disconnect(
1045 d->m_validator, QValidator, SIGNAL(changed()),
1046 this, QQuickTextInput, SLOT(q_validatorChanged()));
1047 }
1048
1049 d->m_validator = v;
1050
1051 if (d->m_validator) {
1052 qmlobject_connect(
1053 d->m_validator, QValidator, SIGNAL(changed()),
1054 this, QQuickTextInput, SLOT(q_validatorChanged()));
1055 }
1056
1057 if (isComponentComplete())
1058 d->checkIsValid();
1059
1060 emit validatorChanged();
1061}
1062
1063void QQuickTextInput::q_validatorChanged()
1064{
1065 Q_D(QQuickTextInput);
1066 d->checkIsValid();
1067}
1068#endif // validator
1069
1070QRectF QQuickTextInputPrivate::anchorRectangle() const
1071{
1072 Q_Q(const QQuickTextInput);
1073 QRectF rect;
1074 int a;
1075 // Unfortunately we cannot use selectionStart() and selectionEnd()
1076 // since they always assume that the selectionStart is logically before selectionEnd.
1077 // To rely on that would cause havoc if the user was interactively moving the end selection
1078 // handle to become before the start selection
1079 if (m_selstart == m_selend)
1080 // This is to handle the case when there is "no selection" while moving the handle onto the
1081 // same position as the other handle (in which case it would hide the selection handles)
1082 a = m_cursor;
1083 else
1084 a = m_selstart == m_cursor ? m_selend : m_selstart;
1085 if (a >= 0) {
1086#if QT_CONFIG(im)
1087 a += m_preeditCursor;
1088#endif
1089 if (m_echoMode == QQuickTextInput::NoEcho)
1090 a = 0;
1091 QTextLine l = m_textLayout.lineForTextPosition(a);
1092 if (l.isValid()) {
1093 qreal x = l.cursorToX(a) - hscroll + q->leftPadding();
1094 qreal y = l.y() - vscroll + q->topPadding();
1095 rect.setRect(x, y, 1, l.height());
1096 }
1097 }
1098 return rect;
1099}
1100
1101void QQuickTextInputPrivate::checkIsValid()
1102{
1103 Q_Q(QQuickTextInput);
1104
1105 ValidatorState state = hasAcceptableInput(m_text);
1106 if (!m_maskData)
1107 m_validInput = state != InvalidInput;
1108 if (state != AcceptableInput) {
1109 if (m_acceptableInput) {
1110 m_acceptableInput = false;
1111 emit q->acceptableInputChanged();
1112 }
1113 } else if (!m_acceptableInput) {
1114 m_acceptableInput = true;
1115 emit q->acceptableInputChanged();
1116 }
1117}
1118
1119/*!
1120 \qmlproperty string QtQuick::TextInput::inputMask
1121
1122 Allows you to set an input mask on the TextInput, restricting the allowable
1123 text inputs. See QLineEdit::inputMask for further details, as the exact
1124 same mask strings are used by TextInput.
1125
1126 \sa acceptableInput, validator
1127*/
1128QString QQuickTextInput::inputMask() const
1129{
1130 Q_D(const QQuickTextInput);
1131 return d->inputMask();
1132}
1133
1134void QQuickTextInput::setInputMask(const QString &im)
1135{
1136 Q_D(QQuickTextInput);
1137 QString canonicalInputMask = im;
1138 if (im.lastIndexOf(QLatin1Char(';')) == -1)
1139 canonicalInputMask.append(QLatin1String("; "));
1140 if (d->inputMask() == canonicalInputMask)
1141 return;
1142
1143 d->setInputMask(im);
1144 emit inputMaskChanged(d->inputMask());
1145}
1146
1147/*!
1148 \qmlproperty bool QtQuick::TextInput::acceptableInput
1149 \readonly
1150
1151 This property is always true unless a validator or input mask has been set.
1152 If a validator or input mask has been set, this property will only be true
1153 if the current text is acceptable to the validator or input mask as a final
1154 string (not as an intermediate string).
1155*/
1156bool QQuickTextInput::hasAcceptableInput() const
1157{
1158 Q_D(const QQuickTextInput);
1159 return d->m_acceptableInput;
1160}
1161
1162/*!
1163 \qmlsignal QtQuick::TextInput::accepted()
1164
1165 This signal is emitted when the Return or Enter key is pressed.
1166 Note that if there is a \l validator or \l inputMask set on the text
1167 input, the signal will only be emitted if the input is in an acceptable
1168 state.
1169
1170 \sa editingFinished(), textEdited()
1171*/
1172
1173/*!
1174 \qmlsignal QtQuick::TextInput::editingFinished()
1175 \since 5.2
1176
1177 This signal is emitted when the Return or Enter key is pressed or
1178 the text input loses focus. Note that if there is a validator or
1179 inputMask set on the text input and enter/return is pressed, this
1180 signal will only be emitted if the input follows
1181 the inputMask and the validator returns an acceptable state.
1182
1183 \sa accepted(), textEdited()
1184*/
1185
1186/*!
1187 \qmlsignal QtQuick::TextInput::textEdited()
1188 \since 5.9
1189
1190 This signal is emitted whenever the text is edited. Unlike \c textChanged(),
1191 this signal is not emitted when the text is changed programmatically, for example,
1192 by changing the value of the \c text property or by calling \c clear().
1193
1194 \sa accepted(), editingFinished()
1195*/
1196
1197#if QT_CONFIG(im)
1198Qt::InputMethodHints QQuickTextInputPrivate::effectiveInputMethodHints() const
1199{
1200 Qt::InputMethodHints hints = inputMethodHints;
1201 if (m_echoMode == QQuickTextInput::NoEcho)
1202 hints |= Qt::ImhHiddenText;
1203 else if (m_echoMode == QQuickTextInput::Password || m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
1204 hints &= ~Qt::ImhHiddenText;
1205 if (m_echoMode != QQuickTextInput::Normal)
1206 hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
1207 return hints;
1208}
1209#endif
1210
1211/*!
1212 \qmlproperty enumeration QtQuick::TextInput::echoMode
1213
1214 Specifies how the text should be displayed in the TextInput.
1215
1216 \value TextInput.Normal Displays the text as it is. (Default)
1217 \value TextInput.Password Displays the \l passwordCharacter instead of the actual characters.
1218 While editing, newly entered characters are displayed in clear text
1219 for a short period specified by the \l passwordMaskDelay property.
1220 \value TextInput.NoEcho Displays nothing.
1221 \value TextInput.PasswordEchoOnEdit Content is masked as with \c TextInput.Password. During
1222 editing, newly entered characters are displayed in clear text as
1223 long as the TextInput has active focus.
1224*/
1225QQuickTextInput::EchoMode QQuickTextInput::echoMode() const
1226{
1227 Q_D(const QQuickTextInput);
1228 return QQuickTextInput::EchoMode(d->m_echoMode);
1229}
1230
1231void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
1232{
1233 Q_D(QQuickTextInput);
1234 if (echoMode() == echo)
1235 return;
1236 d->cancelPasswordEchoTimer();
1237 d->m_echoMode = echo;
1238 d->m_passwordEchoEditing = false;
1239#if QT_CONFIG(im)
1240 updateInputMethod(Qt::ImHints);
1241#endif
1242 d->updateDisplayText();
1243 updateCursorRectangle();
1244
1245 // If this control is used for password input, we want to minimize
1246 // the possibility of string reallocation not to leak (parts of)
1247 // the password.
1248 if (d->m_echoMode != QQuickTextInput::Normal)
1249 d->m_text.reserve(30);
1250
1251 emit echoModeChanged(echoMode());
1252}
1253
1254/*!
1255 \qmlproperty enumeration QtQuick::TextInput::inputMethodHints
1256
1257 Provides hints to the input method about the expected content of the text input and how it
1258 should operate.
1259
1260 The value is a bit-wise combination of flags, or Qt.ImhNone if no hints are set.
1261
1262 Flags that alter behaviour are:
1263
1264 \value Qt.ImhHiddenText Characters should be hidden, as is typically used when entering passwords.
1265
1266 Note that Android IMEs deliberately only
1267 allow Latin/ASCII character input for password fields.
1268 \value Qt.ImhSensitiveData Typed text should not be stored by the active input method
1269 in any persistent storage like predictive user dictionary.
1270
1271 Note that the Qt Framework treats this as a visible password
1272 and in this case Android IMEs deliberately only
1273 allow Latin/ASCII character input.
1274 \value Qt.ImhNoAutoUppercase The input method should not try to automatically switch to
1275 upper case when a sentence ends.
1276 \value Qt.ImhPreferNumbers Numbers are preferred (but not required).
1277 \value Qt.ImhPreferUppercase Upper case letters are preferred (but not required).
1278 \value Qt.ImhPreferLowercase Lower case letters are preferred (but not required).
1279 \value Qt.ImhNoPredictiveText Do not use predictive text (i.e. dictionary lookup) while typing.
1280 \value Qt.ImhDate The text editor functions as a date field.
1281 \value Qt.ImhTime The text editor functions as a time field.
1282
1283 Flags that restrict input (exclusive flags) are:
1284
1285 \value Qt.ImhDigitsOnly Only digits are allowed.
1286 \value Qt.ImhFormattedNumbersOnly Only number input is allowed. This includes decimal point and minus sign.
1287 \value Qt.ImhUppercaseOnly Only upper case letter input is allowed.
1288 \value Qt.ImhLowercaseOnly Only lower case letter input is allowed.
1289 \value Qt.ImhDialableCharactersOnly Only characters suitable for phone dialing are allowed.
1290 \value Qt.ImhEmailCharactersOnly Only characters suitable for email addresses are allowed.
1291 \value Qt.ImhUrlCharactersOnly Only characters suitable for URLs are allowed.
1292
1293 Masks:
1294
1295 \value Qt.ImhExclusiveInputMask This mask yields nonzero if any of the exclusive flags are used.
1296*/
1297
1298Qt::InputMethodHints QQuickTextInput::inputMethodHints() const
1299{
1300#if !QT_CONFIG(im)
1301 return Qt::ImhNone;
1302#else
1303 Q_D(const QQuickTextInput);
1304 return d->inputMethodHints;
1305#endif // im
1306}
1307
1308void QQuickTextInput::setInputMethodHints(Qt::InputMethodHints hints)
1309{
1310#if !QT_CONFIG(im)
1311 Q_UNUSED(hints);
1312#else
1313 Q_D(QQuickTextInput);
1314
1315 if (hints == d->inputMethodHints)
1316 return;
1317
1318 d->inputMethodHints = hints;
1319 updateInputMethod(Qt::ImHints);
1320 emit inputMethodHintsChanged();
1321#endif // im
1322}
1323
1324/*!
1325 \qmlproperty Component QtQuick::TextInput::cursorDelegate
1326 The delegate for the cursor in the TextInput.
1327
1328 If you set a cursorDelegate for a TextInput, this delegate will be used for
1329 drawing the cursor instead of the standard cursor. An instance of the
1330 delegate will be created and managed by the TextInput when a cursor is
1331 needed, and the x property of the delegate instance will be set so as
1332 to be one pixel before the top left of the current character.
1333
1334 Note that the root item of the delegate component must be a QQuickItem or
1335 QQuickItem derived item.
1336*/
1337QQmlComponent* QQuickTextInput::cursorDelegate() const
1338{
1339 Q_D(const QQuickTextInput);
1340 return d->cursorComponent;
1341}
1342
1343void QQuickTextInput::setCursorDelegate(QQmlComponent* c)
1344{
1345 Q_D(QQuickTextInput);
1346 QQuickTextUtil::setCursorDelegate(d, c);
1347}
1348
1349void QQuickTextInput::createCursor()
1350{
1351 Q_D(QQuickTextInput);
1352 d->cursorPending = true;
1353 QQuickTextUtil::createCursor(d);
1354}
1355
1356/*!
1357 \qmlmethod rect QtQuick::TextInput::positionToRectangle(int pos)
1358
1359 This function takes a character position \a pos and returns the rectangle
1360 that the cursor would occupy, if it was placed at that character position.
1361
1362 This is similar to setting the cursorPosition, and then querying the cursor
1363 rectangle, but the cursorPosition is not changed.
1364*/
1365QRectF QQuickTextInput::positionToRectangle(int pos) const
1366{
1367 Q_D(const QQuickTextInput);
1368 if (d->m_echoMode == NoEcho)
1369 pos = 0;
1370#if QT_CONFIG(im)
1371 else if (pos > d->m_cursor)
1372 pos += d->preeditAreaText().size();
1373#endif
1374 QTextLine l = d->m_textLayout.lineForTextPosition(pos);
1375 if (!l.isValid())
1376 return QRectF();
1377 qreal x = l.cursorToX(pos) - d->hscroll;
1378 qreal y = l.y() - d->vscroll;
1379 qreal w = 1;
1380 if (d->overwriteMode) {
1381 if (pos < text().size())
1382 w = l.cursorToX(pos + 1) - x;
1383 else
1384 w = QFontMetrics(font()).horizontalAdvance(QLatin1Char(' ')); // in sync with QTextLine::draw()
1385 }
1386 return QRectF(x, y, w, l.height());
1387}
1388
1389/*!
1390 \qmlmethod int QtQuick::TextInput::positionAt(real x, real y, CursorPosition position)
1391
1392 This function returns the character position at
1393 \a x and \a y pixels from the top left of the textInput. Position 0 is before the
1394 first character, position 1 is after the first character but before the second,
1395 and so on until position text.length, which is after all characters.
1396
1397 This means that for all x values before the first character this function returns 0,
1398 and for all x values after the last character this function returns text.length. If
1399 the y value is above the text the position will be that of the nearest character on
1400 the first line and if it is below the text the position of the nearest character
1401 on the last line will be returned.
1402
1403 The cursor \a position parameter specifies how the cursor position should be resolved:
1404
1405 \value TextInput.CursorBetweenCharacters
1406 Returns the position between characters that is nearest x.
1407 This is the default value.
1408 \value TextInput.CursorOnCharacter
1409 Returns the position before the character that is nearest x.
1410*/
1411
1412int QQuickTextInput::positionAt(qreal x, qreal y, QQuickTextInput::CursorPosition positionQuick) const
1413{
1414 Q_D(const QQuickTextInput);
1415
1416 QTextLine::CursorPosition position = QTextLine::CursorPosition(positionQuick);
1417
1418 int pos = d->positionAt(x, y, position);
1419 const int cursor = d->m_cursor;
1420 if (pos > cursor) {
1421#if QT_CONFIG(im)
1422 const int preeditLength = d->preeditAreaText().size();
1423 pos = pos > cursor + preeditLength
1424 ? pos - preeditLength
1425 : cursor;
1426#else
1427 pos = cursor;
1428#endif
1429 }
1430 return pos;
1431}
1432
1433int QQuickTextInputPrivate::positionAt(qreal x, qreal y, QTextLine::CursorPosition position) const
1434{
1435 Q_Q(const QQuickTextInput);
1436 x += hscroll - q->leftPadding();
1437 y += vscroll - q->topPadding();
1438 QTextLine line = m_textLayout.lineAt(0);
1439 for (int i = 1; i < m_textLayout.lineCount(); ++i) {
1440 QTextLine nextLine = m_textLayout.lineAt(i);
1441
1442 if (y < (line.rect().bottom() + nextLine.y()) / 2)
1443 break;
1444 line = nextLine;
1445 }
1446 return line.isValid() ? line.xToCursor(x, position) : 0;
1447}
1448
1449/*!
1450 \qmlproperty bool QtQuick::TextInput::overwriteMode
1451 \since 5.8
1452
1453 Whether text entered by the user will overwrite existing text.
1454
1455 As with many text editors, the text editor widget can be configured
1456 to insert or overwrite existing text with new text entered by the user.
1457
1458 If this property is \c true, existing text is overwritten, character-for-character
1459 by new text; otherwise, text is inserted at the cursor position, displacing
1460 existing text.
1461
1462 By default, this property is \c false (new text does not overwrite existing text).
1463*/
1464bool QQuickTextInput::overwriteMode() const
1465{
1466 Q_D(const QQuickTextInput);
1467 return d->overwriteMode;
1468}
1469
1470void QQuickTextInput::setOverwriteMode(bool overwrite)
1471{
1472 Q_D(QQuickTextInput);
1473 if (d->overwriteMode == overwrite)
1474 return;
1475 d->overwriteMode = overwrite;
1476 emit overwriteModeChanged(overwrite);
1477}
1478
1479void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
1480{
1481 Q_D(QQuickTextInput);
1482 // Don't allow MacOSX up/down support, and we don't allow a completer.
1483 bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier;
1484 if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
1485 // Ignore when moving off the end unless there is a selection,
1486 // because then moving will do something (deselect).
1487 int cursorPosition = d->m_cursor;
1488 if (cursorPosition == 0)
1489 ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
1490 if (!ignore && cursorPosition == d->m_text.size())
1491 ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
1492 }
1493 if (ignore) {
1494 ev->ignore();
1495 } else {
1496 d->processKeyEvent(ev);
1497 }
1498 if (!ev->isAccepted())
1499 QQuickImplicitSizeItem::keyPressEvent(ev);
1500}
1501
1502#if QT_CONFIG(im)
1503void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
1504{
1505 Q_D(QQuickTextInput);
1506 const bool wasComposing = d->hasImState;
1507 d->processInputMethodEvent(ev);
1508 if (!ev->isAccepted())
1509 QQuickImplicitSizeItem::inputMethodEvent(ev);
1510
1511 if (wasComposing != d->hasImState)
1512 emit inputMethodComposingChanged();
1513}
1514#endif
1515
1516void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event)
1517{
1518 Q_D(QQuickTextInput);
1519
1520 if (d->selectByMouse && event->button() == Qt::LeftButton &&
1521 QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event)) {
1522#if QT_CONFIG(im)
1523 d->commitPreedit();
1524#endif
1525 int cursor = d->positionAt(event->position());
1526 d->selectWordAtPos(cursor);
1527 event->setAccepted(true);
1528 if (!d->hasPendingTripleClick()) {
1529 d->tripleClickStartPoint = event->position();
1530 d->tripleClickTimer.start();
1531 }
1532 } else {
1533 if (d->sendMouseEventToInputContext(event))
1534 return;
1535 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
1536 }
1537}
1538
1539void QQuickTextInput::mousePressEvent(QMouseEvent *event)
1540{
1541 Q_D(QQuickTextInput);
1542
1543 d->pressPos = event->position();
1544
1545 if (d->sendMouseEventToInputContext(event))
1546 return;
1547
1548 d->hadSelectionOnMousePress = d->hasSelectedText();
1549
1550 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event);
1551 if (d->selectByMouse &&
1552 (isMouse
1553#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1554 || d->selectByTouchDrag
1555#endif
1556 )) {
1557 setKeepMouseGrab(false);
1558 d->selectPressed = true;
1559 QPointF distanceVector = d->pressPos - d->tripleClickStartPoint;
1560 if (d->hasPendingTripleClick()
1561 && distanceVector.manhattanLength() < QGuiApplication::styleHints()->startDragDistance()) {
1562 event->setAccepted(true);
1563 selectAll();
1564 return;
1565 }
1566 }
1567
1568 if (isMouse
1569#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1570 || d->selectByTouchDrag
1571#endif
1572 ) {
1573 bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
1574 int cursor = d->positionAt(event->position());
1575 d->moveCursor(cursor, mark);
1576 }
1577
1578 if (d->focusOnPress && !qGuiApp->styleHints()->setFocusOnTouchRelease())
1579 ensureActiveFocus(Qt::MouseFocusReason);
1580
1581 event->setAccepted(true);
1582}
1583
1584void QQuickTextInput::mouseMoveEvent(QMouseEvent *event)
1585{
1586 Q_D(QQuickTextInput);
1587 if (!QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event)
1588#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1589 && ! d->selectByTouchDrag
1590#endif
1591 )
1592 return;
1593
1594 if (d->selectPressed) {
1595 if (qAbs(int(event->position().x() - d->pressPos.x())) > QGuiApplication::styleHints()->startDragDistance())
1596 setKeepMouseGrab(true);
1597
1598#if QT_CONFIG(im)
1599 if (d->composeMode()) {
1600 // start selection
1601 int startPos = d->positionAt(d->pressPos);
1602 int currentPos = d->positionAt(event->position());
1603 if (startPos != currentPos)
1604 d->setSelection(startPos, currentPos - startPos);
1605 } else
1606#endif
1607 {
1608 moveCursorSelection(d->positionAt(event->position()), d->mouseSelectionMode);
1609 }
1610 event->setAccepted(true);
1611 } else {
1612 QQuickImplicitSizeItem::mouseMoveEvent(event);
1613 }
1614}
1615
1616void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
1617{
1618 Q_D(QQuickTextInput);
1619 if (d->sendMouseEventToInputContext(event))
1620 return;
1621 if (d->selectPressed) {
1622 d->selectPressed = false;
1623 setKeepMouseGrab(false);
1624 }
1625 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event)
1626#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1627 || d->selectByTouchDrag
1628#endif
1629 ;
1630
1631#if QT_CONFIG(clipboard)
1632 if (isMouse && QGuiApplication::clipboard()->supportsSelection()) {
1633 if (event->button() == Qt::LeftButton) {
1634 d->copy(QClipboard::Selection);
1635 } else if (!d->m_readOnly && event->button() == Qt::MiddleButton) {
1636 d->deselect();
1637 d->insert(QGuiApplication::clipboard()->text(QClipboard::Selection));
1638 }
1639 }
1640#endif
1641
1642 // On a touchscreen or with a stylus, set cursor position and focus on release, not on press;
1643 // if Flickable steals the grab in the meantime, the cursor won't move.
1644 // Check d->hasSelectedText() to keep touch-and-hold word selection working.
1645 // But if text was selected already on press, deselect it on release.
1646 if (!isMouse && (!d->hasSelectedText() || d->hadSelectionOnMousePress))
1647 d->moveCursor(d->positionAt(event->position()), false);
1648 // On Android, after doing a long-press to start selection, we see a release event,
1649 // even though there was no press event. So reset hadSelectionOnMousePress to avoid
1650 // it getting stuck in true state.
1651 d->hadSelectionOnMousePress = false;
1652
1653 if (d->focusOnPress && qGuiApp->styleHints()->setFocusOnTouchRelease())
1654 ensureActiveFocus(Qt::MouseFocusReason);
1655
1656 if (!event->isAccepted())
1657 QQuickImplicitSizeItem::mouseReleaseEvent(event);
1658}
1659
1660#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1661bool QQuickTextInputPrivate::handleContextMenuEvent(QContextMenuEvent *event)
1662#else
1663bool QQuickTextInput::contextMenuEvent(QContextMenuEvent *event)
1664#endif
1665{
1666 Q_Q(QQuickTextInput);
1667 QContextMenuEvent mapped(event->reason(),
1668 q->mapToScene(q->cursorRectangle().center()).toPoint(), event->globalPos(),
1669 event->modifiers());
1670 const bool eventProcessed = QQuickItemPrivate::handleContextMenuEvent(&mapped);
1671 event->setAccepted(mapped.isAccepted());
1672 return eventProcessed;
1673}
1674
1675bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event)
1676{
1677#if QT_CONFIG(im)
1678 if (composeMode()) {
1679 int tmp_cursor = positionAt(event->position());
1680 int mousePos = tmp_cursor - m_cursor;
1681 if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().size()) {
1682 if (event->type() == QEvent::MouseButtonRelease) {
1683 QGuiApplication::inputMethod()->invokeAction(QInputMethod::Click, mousePos);
1684 }
1685 return true;
1686 }
1687 }
1688#else
1689 Q_UNUSED(event);
1690#endif
1691
1692 return false;
1693}
1694
1695void QQuickTextInput::mouseUngrabEvent()
1696{
1697 Q_D(QQuickTextInput);
1698 d->selectPressed = false;
1699 setKeepMouseGrab(false);
1700}
1701
1702bool QQuickTextInput::event(QEvent* ev)
1703{
1704#if QT_CONFIG(shortcut)
1705 Q_D(QQuickTextInput);
1706 if (ev->type() == QEvent::ShortcutOverride) {
1707 if (d->m_readOnly) {
1708 ev->ignore();
1709 return false;
1710 }
1711 QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
1712 if (ke == QKeySequence::Copy
1713 || ke == QKeySequence::Paste
1714 || ke == QKeySequence::Cut
1715 || ke == QKeySequence::Redo
1716 || ke == QKeySequence::Undo
1717 || ke == QKeySequence::MoveToNextWord
1718 || ke == QKeySequence::MoveToPreviousWord
1719 || ke == QKeySequence::MoveToStartOfDocument
1720 || ke == QKeySequence::MoveToEndOfDocument
1721 || ke == QKeySequence::SelectNextWord
1722 || ke == QKeySequence::SelectPreviousWord
1723 || ke == QKeySequence::SelectStartOfLine
1724 || ke == QKeySequence::SelectEndOfLine
1725 || ke == QKeySequence::SelectStartOfBlock
1726 || ke == QKeySequence::SelectEndOfBlock
1727 || ke == QKeySequence::SelectStartOfDocument
1728 || ke == QKeySequence::SelectAll
1729 || ke == QKeySequence::SelectEndOfDocument
1730 || ke == QKeySequence::DeleteCompleteLine) {
1731 ke->accept();
1732 return true;
1733 } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1734 || ke->modifiers() == Qt::KeypadModifier) {
1735 if (ke->key() < Qt::Key_Escape) {
1736 ke->accept();
1737 return true;
1738 } else {
1739 switch (ke->key()) {
1740 case Qt::Key_Delete:
1741 case Qt::Key_Home:
1742 case Qt::Key_End:
1743 case Qt::Key_Backspace:
1744 case Qt::Key_Left:
1745 case Qt::Key_Right:
1746 ke->accept();
1747 return true;
1748 default:
1749 break;
1750 }
1751 }
1752 }
1753 ev->ignore();
1754 }
1755#endif
1756
1757 return QQuickImplicitSizeItem::event(ev);
1758}
1759
1760void QQuickTextInput::geometryChange(const QRectF &newGeometry,
1761 const QRectF &oldGeometry)
1762{
1763 Q_D(QQuickTextInput);
1764 if (!d->inLayout) {
1765 if (newGeometry.width() != oldGeometry.width())
1766 d->updateLayout();
1767 else if (newGeometry.height() != oldGeometry.height() && d->vAlign != QQuickTextInput::AlignTop)
1768 d->updateBaselineOffset();
1769 updateCursorRectangle();
1770 }
1771 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
1772}
1773
1774void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
1775{
1776 Q_D(QQuickTextInput);
1777 Q_UNUSED(value);
1778 switch (change) {
1779 case ItemDevicePixelRatioHasChanged:
1780 if (d->containsUnscalableGlyphs) {
1781 // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
1782 // Text layout code respects the current device pixel ratio automatically, we only need
1783 // to rerun layout after the ratio changed.
1784 d->updateLayout();
1785 }
1786 break;
1787
1788 default:
1789 break;
1790 }
1791 QQuickImplicitSizeItem::itemChange(change, value);
1792}
1793
1794void QQuickTextInputPrivate::ensureVisible(int position, int preeditCursor, int preeditLength)
1795{
1796 Q_Q(QQuickTextInput);
1797 QTextLine textLine = m_textLayout.lineForTextPosition(position + preeditCursor);
1798 const qreal width = qMax<qreal>(0, q->width() - q->leftPadding() - q->rightPadding());
1799 qreal cix = 0;
1800 qreal widthUsed = 0;
1801 if (textLine.isValid()) {
1802 cix = textLine.cursorToX(position + preeditLength);
1803 const qreal cursorWidth = cix >= 0 ? cix : width - cix;
1804 widthUsed = qMax(textLine.naturalTextWidth(), cursorWidth);
1805 }
1806 int previousScroll = hscroll;
1807
1808 if (widthUsed <= width) {
1809 hscroll = 0;
1810 } else {
1811 Q_ASSERT(textLine.isValid());
1812 if (cix - hscroll >= width) {
1813 // text doesn't fit, cursor is to the right of br (scroll right)
1814 hscroll = cix - width;
1815 } else if (cix - hscroll < 0 && hscroll < widthUsed) {
1816 // text doesn't fit, cursor is to the left of br (scroll left)
1817 hscroll = cix;
1818 } else if (widthUsed - hscroll < width) {
1819 // text doesn't fit, text document is to the left of br; align
1820 // right
1821 hscroll = widthUsed - width;
1822 } else if (width - hscroll > widthUsed) {
1823 // text doesn't fit, text document is to the right of br; align
1824 // left
1825 hscroll = width - widthUsed;
1826 }
1827#if QT_CONFIG(im)
1828 if (preeditLength > 0) {
1829 // check to ensure long pre-edit text doesn't push the cursor
1830 // off to the left
1831 cix = textLine.cursorToX(position + qMax(0, preeditCursor - 1));
1832 if (cix < hscroll)
1833 hscroll = cix;
1834 }
1835#endif
1836 }
1837 if (previousScroll != hscroll)
1838 textLayoutDirty = true;
1839}
1840
1841void QQuickTextInputPrivate::updateHorizontalScroll()
1842{
1843 if (autoScroll && m_echoMode != QQuickTextInput::NoEcho) {
1844#if QT_CONFIG(im)
1845 const int preeditLength = m_textLayout.preeditAreaText().size();
1846 ensureVisible(m_cursor, m_preeditCursor, preeditLength);
1847#else
1848 ensureVisible(m_cursor);
1849#endif
1850 } else {
1851 hscroll = 0;
1852 }
1853}
1854
1855void QQuickTextInputPrivate::updateVerticalScroll()
1856{
1857 Q_Q(QQuickTextInput);
1858#if QT_CONFIG(im)
1859 const int preeditLength = m_textLayout.preeditAreaText().size();
1860#endif
1861 const qreal height = qMax<qreal>(0, q->height() - q->topPadding() - q->bottomPadding());
1862 qreal heightUsed = contentSize.height();
1863 qreal previousScroll = vscroll;
1864
1865 if (!autoScroll || heightUsed <= height) {
1866 // text fits in br; use vscroll for alignment
1867 vscroll = -QQuickTextUtil::alignedY(
1868 heightUsed, height, vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask));
1869 } else {
1870#if QT_CONFIG(im)
1871 QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + preeditLength);
1872#else
1873 QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor);
1874#endif
1875 QRectF r = currentLine.isValid() ? currentLine.rect() : QRectF();
1876 qreal top = r.top();
1877 int bottom = r.bottom();
1878
1879 if (bottom - vscroll >= height) {
1880 // text doesn't fit, cursor is to the below the br (scroll down)
1881 vscroll = bottom - height;
1882 } else if (top - vscroll < 0 && vscroll < heightUsed) {
1883 // text doesn't fit, cursor is above br (scroll up)
1884 vscroll = top;
1885 } else if (heightUsed - vscroll < height) {
1886 // text doesn't fit, text document is to the left of br; align
1887 // right
1888 vscroll = heightUsed - height;
1889 }
1890#if QT_CONFIG(im)
1891 if (preeditLength > 0) {
1892 // check to ensure long pre-edit text doesn't push the cursor
1893 // off the top
1894 currentLine = m_textLayout.lineForTextPosition(m_cursor + qMax(0, m_preeditCursor - 1));
1895 top = currentLine.isValid() ? currentLine.rect().top() : 0;
1896 if (top < vscroll)
1897 vscroll = top;
1898 }
1899#endif
1900 }
1901 if (previousScroll != vscroll)
1902 textLayoutDirty = true;
1903}
1904
1905void QQuickTextInput::triggerPreprocess()
1906{
1907 Q_D(QQuickTextInput);
1908 if (d->updateType == QQuickTextInputPrivate::UpdateNone)
1909 d->updateType = QQuickTextInputPrivate::UpdateOnlyPreprocess;
1910 polish();
1911 update();
1912}
1913
1914void QQuickTextInput::updatePolish()
1915{
1916 invalidateFontCaches();
1917}
1918
1919void QQuickTextInput::invalidateFontCaches()
1920{
1921 Q_D(QQuickTextInput);
1922
1923 if (d->m_textLayout.engine() != nullptr)
1924 d->m_textLayout.engine()->resetFontEngineCache();
1925}
1926
1927void QQuickTextInput::ensureActiveFocus(Qt::FocusReason reason)
1928{
1929 bool hadActiveFocus = hasActiveFocus();
1930 forceActiveFocus(reason);
1931#if QT_CONFIG(im)
1932 Q_D(QQuickTextInput);
1933 // re-open input panel on press if already focused
1934 if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
1935 qGuiApp->inputMethod()->show();
1936#else
1937 Q_UNUSED(hadActiveFocus);
1938#endif
1939}
1940
1941QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1942{
1943 Q_UNUSED(data);
1944 Q_D(QQuickTextInput);
1945
1946 if (d->updateType != QQuickTextInputPrivate::UpdatePaintNode && oldNode != nullptr) {
1947 // Update done in preprocess() in the nodes
1948 d->updateType = QQuickTextInputPrivate::UpdateNone;
1949 return oldNode;
1950 }
1951
1952 d->updateType = QQuickTextInputPrivate::UpdateNone;
1953
1954 QSGInternalTextNode *node = static_cast<QSGInternalTextNode *>(oldNode);
1955 if (node == nullptr)
1956 node = d->sceneGraphContext()->createInternalTextNode(d->sceneGraphRenderContext());
1957 d->textNode = node;
1958
1959 const bool showCursor = !isReadOnly() && d->cursorItem == nullptr && d->cursorVisible && d->m_blinkStatus;
1960
1961 if (!d->textLayoutDirty && oldNode != nullptr) {
1962 if (showCursor)
1963 node->setCursor(cursorRectangle(), d->color, nullptr);
1964 else
1965 node->clearCursor();
1966 } else {
1967 node->setRenderType(QSGTextNode::RenderType(d->renderType));
1968
1969 QSGInternalTextNode::RecycleBin recycleBin;
1970 node->recycle(&recycleBin);
1971 node->setMatrix(QMatrix4x4());
1972 node->setTextStyle(QSGInternalTextNode::Normal);
1973 node->setColor(d->color);
1974 node->setSelectionTextColor(d->selectedTextColor);
1975 node->setSelectionColor(d->selectionColor);
1976 node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
1977
1978 if (flags().testFlag(ItemObservesViewport))
1979 node->setViewport(clipRect());
1980 else
1981 node->setViewport(QRectF{});
1982
1983 QPointF offset(leftPadding(), topPadding());
1984 offset -= QPointF(d->hscroll, d->vscroll);
1985
1986 if (!d->m_textLayout.text().isEmpty()
1987#if QT_CONFIG(im)
1988 || !d->m_textLayout.preeditAreaText().isEmpty()
1989#endif
1990 ) {
1991 node->addTextLayout(offset,
1992 &d->m_textLayout,
1993 &recycleBin,
1994 d->selectionStart(),
1995 d->selectionEnd() - 1); // selectionEnd() returns first char after
1996 // selection
1997 }
1998
1999 if (showCursor)
2000 node->setCursor(cursorRectangle(), d->color, &recycleBin);
2001 node->discardUnusedNodes(&recycleBin);
2002
2003 d->textLayoutDirty = false;
2004 }
2005
2006 d->containsUnscalableGlyphs = node->containsUnscalableGlyphs();
2007
2008 invalidateFontCaches();
2009
2010 return node;
2011}
2012
2013#if QT_CONFIG(im)
2014QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
2015{
2016#ifdef Q_OS_ANDROID
2017 // QTBUG-61652
2018 if (property == Qt::ImEnterKeyType) {
2019 Q_D(const QQuickItem);
2020 // Do not change if type was set manually
2021 if (!d->extra.isAllocated()
2022 || d->extra->enterKeyAttached == nullptr
2023 || d->extra->enterKeyAttached->type() == Qt::EnterKeyDefault) {
2024
2025 QQuickItem *next = const_cast<QQuickTextInput*>(this)->nextItemInFocusChain();
2026 QQuickItem *originalNext = next;
2027 while (next && next != this && !next->activeFocusOnTab()) {
2028 next = next->nextItemInFocusChain();
2029 if (next == originalNext) {
2030 // There seems to be no suitable element in the focus chain
2031 next = nullptr;
2032 }
2033 }
2034 if (next) {
2035 const auto nextYPos = next->mapToGlobal(QPoint(0, 0)).y();
2036 const auto currentYPos = this->mapToGlobal(QPoint(0, 0)).y();
2037 if (currentYPos < nextYPos)
2038 // Set EnterKey to KeyNext type only if the next item
2039 // in the focus chain is below current QQuickTextInput
2040 return Qt::EnterKeyNext;
2041 }
2042 }
2043 }
2044#endif
2045 return inputMethodQuery(property, QVariant());
2046}
2047
2048QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property, const QVariant &argument) const
2049{
2050 Q_D(const QQuickTextInput);
2051 switch (property) {
2052 case Qt::ImEnabled:
2053 return QVariant((bool)(flags() & ItemAcceptsInputMethod));
2054 case Qt::ImHints:
2055 return QVariant((int) d->effectiveInputMethodHints());
2056 case Qt::ImCursorRectangle:
2057 return cursorRectangle();
2058 case Qt::ImAnchorRectangle:
2059 return d->anchorRectangle();
2060 case Qt::ImFont:
2061 return font();
2062 case Qt::ImCursorPosition: {
2063 const QPointF pt = argument.toPointF();
2064 if (!pt.isNull())
2065 return QVariant(d->positionAt(pt));
2066 return QVariant(d->m_cursor);
2067 }
2068 case Qt::ImSurroundingText:
2069 if (d->m_echoMode == PasswordEchoOnEdit && !d->m_passwordEchoEditing) {
2070 return QVariant(displayText());
2071 } else {
2072 return QVariant(d->realText());
2073 }
2074 case Qt::ImCurrentSelection:
2075 return QVariant(selectedText());
2076 case Qt::ImMaximumTextLength:
2077 return QVariant(maxLength());
2078 case Qt::ImAnchorPosition:
2079 if (d->selectionStart() == d->selectionEnd())
2080 return QVariant(d->m_cursor);
2081 else if (d->selectionStart() == d->m_cursor)
2082 return QVariant(d->selectionEnd());
2083 else
2084 return QVariant(d->selectionStart());
2085 case Qt::ImAbsolutePosition:
2086 return QVariant(d->m_cursor);
2087 case Qt::ImTextAfterCursor:
2088 if (argument.isValid())
2089 return QVariant(d->m_text.mid(d->m_cursor, argument.toInt()));
2090 return QVariant(d->m_text.mid(d->m_cursor));
2091 case Qt::ImTextBeforeCursor:
2092 if (argument.isValid())
2093 return QVariant(QStringView{d->m_text}.left(d->m_cursor).right(argument.toInt()).toString());
2094 return QVariant(d->m_text.left(d->m_cursor));
2095 case Qt::ImReadOnly:
2096 return QVariant(d->m_readOnly);
2097 default:
2098 return QQuickItem::inputMethodQuery(property);
2099 }
2100}
2101#endif // im
2102
2103/*!
2104 \qmlmethod void QtQuick::TextInput::deselect()
2105
2106 Removes active text selection.
2107*/
2108void QQuickTextInput::deselect()
2109{
2110 Q_D(QQuickTextInput);
2111 d->deselect();
2112}
2113
2114/*!
2115 \qmlmethod void QtQuick::TextInput::selectAll()
2116
2117 Causes all text to be selected.
2118*/
2119void QQuickTextInput::selectAll()
2120{
2121 Q_D(QQuickTextInput);
2122 d->setSelection(0, text().size());
2123}
2124
2125/*!
2126 \qmlmethod bool QtQuick::TextInput::isRightToLeft(int start, int end)
2127
2128 Returns \c true if the natural reading direction of the editor text
2129 found between positions \a start and \a end is right to left.
2130*/
2131bool QQuickTextInput::isRightToLeft(int start, int end)
2132{
2133 if (start > end) {
2134 qmlWarning(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
2135 return false;
2136 } else {
2137 return QStringView{text()}.mid(start, end - start).isRightToLeft();
2138 }
2139}
2140
2141#if QT_CONFIG(clipboard)
2142/*!
2143 \qmlmethod void QtQuick::TextInput::cut()
2144
2145 Moves the currently selected text to the system clipboard.
2146
2147 \note If the echo mode is set to a mode other than Normal then cut
2148 will not work. This is to prevent using cut as a method of bypassing
2149 password features of the line control.
2150*/
2151void QQuickTextInput::cut()
2152{
2153 Q_D(QQuickTextInput);
2154 if (!d->m_readOnly && d->m_echoMode == QQuickTextInput::Normal) {
2155 d->copy();
2156 d->del();
2157 }
2158}
2159
2160/*!
2161 \qmlmethod void QtQuick::TextInput::copy()
2162
2163 Copies the currently selected text to the system clipboard.
2164
2165 \note If the echo mode is set to a mode other than Normal then copy
2166 will not work. This is to prevent using copy as a method of bypassing
2167 password features of the line control.
2168*/
2169void QQuickTextInput::copy()
2170{
2171 Q_D(QQuickTextInput);
2172 d->copy();
2173}
2174
2175/*!
2176 \qmlmethod void QtQuick::TextInput::paste()
2177
2178 Replaces the currently selected text by the contents of the system clipboard.
2179*/
2180void QQuickTextInput::paste()
2181{
2182 Q_D(QQuickTextInput);
2183 if (!d->m_readOnly)
2184 d->paste();
2185}
2186#endif // clipboard
2187
2188/*!
2189 \qmlmethod void QtQuick::TextInput::undo()
2190
2191 Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
2192 current selection, and updates the selection start to the current cursor
2193 position.
2194*/
2195
2196void QQuickTextInput::undo()
2197{
2198 Q_D(QQuickTextInput);
2199 if (!d->m_readOnly) {
2200 d->cancelInput();
2201 d->internalUndo();
2202 d->finishChange(-1, true);
2203 }
2204}
2205
2206/*!
2207 \qmlmethod void QtQuick::TextInput::redo()
2208
2209 Redoes the last operation if redo is \l {canRedo}{available}.
2210*/
2211
2212void QQuickTextInput::redo()
2213{
2214 Q_D(QQuickTextInput);
2215 if (!d->m_readOnly) {
2216 d->cancelInput();
2217 d->internalRedo();
2218 d->finishChange();
2219 }
2220}
2221
2222/*!
2223 \qmlmethod void QtQuick::TextInput::insert(int position, string text)
2224
2225 Inserts \a text into the TextInput at \a position.
2226*/
2227
2228void QQuickTextInput::insert(int position, const QString &text)
2229{
2230 Q_D(QQuickTextInput);
2231 if (d->m_echoMode == QQuickTextInput::Password) {
2232 if (d->m_passwordMaskDelay > 0)
2233 d->m_passwordEchoTimer.start(d->m_passwordMaskDelay, this);
2234 }
2235 if (position < 0 || position > d->m_text.size())
2236 return;
2237
2238 const int priorState = d->m_undoState;
2239
2240 QString insertText = text;
2241
2242 if (d->hasSelectedText()) {
2243 d->addCommand(QQuickTextInputPrivate::Command(
2244 QQuickTextInputPrivate::SetSelection, d->m_cursor, u'\0', d->m_selstart, d->m_selend));
2245 }
2246 if (d->m_maskData) {
2247 insertText = d->maskString(position, insertText);
2248 for (int i = 0; i < insertText.size(); ++i) {
2249 d->addCommand(QQuickTextInputPrivate::Command(
2250 QQuickTextInputPrivate::DeleteSelection, position + i, d->m_text.at(position + i), -1, -1));
2251 d->addCommand(QQuickTextInputPrivate::Command(
2252 QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
2253 }
2254 d->m_text.replace(position, insertText.size(), insertText);
2255 if (!insertText.isEmpty())
2256 d->m_textDirty = true;
2257 if (position < d->m_selend && position + insertText.size() > d->m_selstart)
2258 d->m_selDirty = true;
2259 } else {
2260 int remaining = d->m_maxLength - d->m_text.size();
2261 if (remaining != 0) {
2262 insertText = insertText.left(remaining);
2263 d->m_text.insert(position, insertText);
2264 for (int i = 0; i < insertText.size(); ++i)
2265 d->addCommand(QQuickTextInputPrivate::Command(
2266 QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
2267 if (d->m_cursor >= position)
2268 d->m_cursor += insertText.size();
2269 if (d->m_selstart >= position)
2270 d->m_selstart += insertText.size();
2271 if (d->m_selend >= position)
2272 d->m_selend += insertText.size();
2273 d->m_textDirty = true;
2274 if (position >= d->m_selstart && position <= d->m_selend)
2275 d->m_selDirty = true;
2276 }
2277 }
2278
2279 d->addCommand(QQuickTextInputPrivate::Command(
2280 QQuickTextInputPrivate::SetSelection, d->m_cursor, u'\0', d->m_selstart, d->m_selend));
2281 d->finishChange(priorState);
2282
2283 if (d->lastSelectionStart != d->lastSelectionEnd) {
2284 if (d->m_selstart != d->lastSelectionStart) {
2285 d->lastSelectionStart = d->m_selstart;
2286 emit selectionStartChanged();
2287 }
2288 if (d->m_selend != d->lastSelectionEnd) {
2289 d->lastSelectionEnd = d->m_selend;
2290 emit selectionEndChanged();
2291 }
2292 }
2293}
2294
2295/*!
2296 \qmlmethod void QtQuick::TextInput::remove(int start, int end)
2297
2298 Removes the section of text that is between the \a start and \a end positions from the TextInput.
2299*/
2300
2301void QQuickTextInput::remove(int start, int end)
2302{
2303 Q_D(QQuickTextInput);
2304
2305 start = qBound(0, start, d->m_text.size());
2306 end = qBound(0, end, d->m_text.size());
2307
2308 if (start > end)
2309 qSwap(start, end);
2310 else if (start == end)
2311 return;
2312
2313 if (start < d->m_selend && end > d->m_selstart)
2314 d->m_selDirty = true;
2315
2316 const int priorState = d->m_undoState;
2317
2318 d->addCommand(QQuickTextInputPrivate::Command(
2319 QQuickTextInputPrivate::SetSelection, d->m_cursor, u'\0', d->m_selstart, d->m_selend));
2320
2321 if (start <= d->m_cursor && d->m_cursor < end) {
2322 // cursor is within the selection. Split up the commands
2323 // to be able to restore the correct cursor position
2324 for (int i = d->m_cursor; i >= start; --i) {
2325 d->addCommand(QQuickTextInputPrivate::Command(
2326 QQuickTextInputPrivate::DeleteSelection, i, d->m_text.at(i), -1, 1));
2327 }
2328 for (int i = end - 1; i > d->m_cursor; --i) {
2329 d->addCommand(QQuickTextInputPrivate::Command(
2330 QQuickTextInputPrivate::DeleteSelection, i - d->m_cursor + start - 1, d->m_text.at(i), -1, -1));
2331 }
2332 } else {
2333 for (int i = end - 1; i >= start; --i) {
2334 d->addCommand(QQuickTextInputPrivate::Command(
2335 QQuickTextInputPrivate::RemoveSelection, i, d->m_text.at(i), -1, -1));
2336 }
2337 }
2338 if (d->m_maskData) {
2339 d->m_text.replace(start, end - start, d->clearString(start, end - start));
2340 for (int i = 0; i < end - start; ++i) {
2341 d->addCommand(QQuickTextInputPrivate::Command(
2342 QQuickTextInputPrivate::Insert, start + i, d->m_text.at(start + i), -1, -1));
2343 }
2344 } else {
2345 d->m_text.remove(start, end - start);
2346
2347 if (d->m_cursor > start)
2348 d->m_cursor -= qMin(d->m_cursor, end) - start;
2349 if (d->m_selstart > start)
2350 d->m_selstart -= qMin(d->m_selstart, end) - start;
2351 if (d->m_selend >= end)
2352 d->m_selend -= end - start;
2353 }
2354 d->addCommand(QQuickTextInputPrivate::Command(
2355 QQuickTextInputPrivate::SetSelection, d->m_cursor, u'\0', d->m_selstart, d->m_selend));
2356
2357 d->m_textDirty = true;
2358 d->finishChange(priorState);
2359
2360 if (d->lastSelectionStart != d->lastSelectionEnd) {
2361 if (d->m_selstart != d->lastSelectionStart) {
2362 d->lastSelectionStart = d->m_selstart;
2363 emit selectionStartChanged();
2364 }
2365 if (d->m_selend != d->lastSelectionEnd) {
2366 d->lastSelectionEnd = d->m_selend;
2367 emit selectionEndChanged();
2368 }
2369 }
2370}
2371
2372
2373/*!
2374 \qmlmethod void QtQuick::TextInput::selectWord()
2375
2376 Causes the word closest to the current cursor position to be selected.
2377*/
2378void QQuickTextInput::selectWord()
2379{
2380 Q_D(QQuickTextInput);
2381 d->selectWordAtPos(d->m_cursor);
2382}
2383
2384/*!
2385 \qmlproperty string QtQuick::TextInput::passwordCharacter
2386
2387 This is the character displayed when echoMode is set to Password or
2388 PasswordEchoOnEdit. By default it is the password character used by
2389 the platform theme.
2390
2391 If this property is set to a string with more than one character,
2392 the first character is used. If the string is empty, the value
2393 is ignored and the property is not set.
2394*/
2395QString QQuickTextInput::passwordCharacter() const
2396{
2397 Q_D(const QQuickTextInput);
2398 return QString(d->m_passwordCharacter);
2399}
2400
2401void QQuickTextInput::setPasswordCharacter(const QString &str)
2402{
2403 Q_D(QQuickTextInput);
2404 if (str.size() < 1)
2405 return;
2406 d->m_passwordCharacter = str.constData()[0];
2407 if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit)
2408 d->updateDisplayText();
2409 emit passwordCharacterChanged();
2410}
2411
2412/*!
2413 \qmlproperty int QtQuick::TextInput::passwordMaskDelay
2414 \since 5.4
2415
2416 Sets the delay before visible character is masked with password character, in milliseconds.
2417
2418 The reset method will be called by assigning undefined.
2419*/
2420int QQuickTextInput::passwordMaskDelay() const
2421{
2422 Q_D(const QQuickTextInput);
2423 return d->m_passwordMaskDelay;
2424}
2425
2426void QQuickTextInput::setPasswordMaskDelay(int delay)
2427{
2428 Q_D(QQuickTextInput);
2429 if (d->m_passwordMaskDelay != delay) {
2430 d->m_passwordMaskDelay = delay;
2431 emit passwordMaskDelayChanged(delay);
2432 }
2433}
2434
2435void QQuickTextInput::resetPasswordMaskDelay()
2436{
2437 setPasswordMaskDelay(qGuiApp->styleHints()->passwordMaskDelay());
2438}
2439
2440/*!
2441 \qmlproperty string QtQuick::TextInput::displayText
2442
2443 This is the text displayed in the TextInput.
2444
2445 If \l echoMode is set to TextInput::Normal, this holds the
2446 same value as the TextInput::text property. Otherwise,
2447 this property holds the text visible to the user, while
2448 the \l text property holds the actual entered text.
2449
2450 \note Unlike the TextInput::text property, this contains
2451 partial text input from an input method.
2452
2453 \readonly
2454 \sa preeditText
2455*/
2456QString QQuickTextInput::displayText() const
2457{
2458 Q_D(const QQuickTextInput);
2459 return d->m_textLayout.text().insert(d->m_textLayout.preeditAreaPosition(), d->m_textLayout.preeditAreaText());
2460}
2461
2462/*!
2463 \qmlproperty string QtQuick::TextInput::preeditText
2464 \readonly
2465 \since 5.7
2466
2467 This property contains partial text input from an input method.
2468
2469 To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
2470 flag in inputMethodHints.
2471
2472 \sa displayText, inputMethodHints
2473*/
2474QString QQuickTextInput::preeditText() const
2475{
2476 Q_D(const QQuickTextInput);
2477 return d->m_textLayout.preeditAreaText();
2478}
2479
2480/*!
2481 \qmlproperty bool QtQuick::TextInput::selectByMouse
2482
2483 Defaults to \c true.
2484
2485 If true, the user can use the mouse to select text in the usual way.
2486
2487 \note In versions prior to 6.4, the default was \c false; but if you
2488 enabled this property, you could also select text on a touchscreen by
2489 dragging your finger across it. This interfered with flicking when
2490 TextInput was used inside a Flickable. For consistency with TextField,
2491 selectByMouse now really means what it says: if \c true, you can select
2492 text by dragging \e only with a mouse. If this change does not suit your
2493 application, you can set \c selectByMouse to \c false, or import an older
2494 API version (for example \c {import QtQuick 6.3}) to revert to the previous
2495 behavior. The option to revert behavior by changing the import version will
2496 be removed in a later version of Qt.
2497*/
2498bool QQuickTextInput::selectByMouse() const
2499{
2500 Q_D(const QQuickTextInput);
2501 return d->selectByMouse;
2502}
2503
2504void QQuickTextInput::setSelectByMouse(bool on)
2505{
2506 Q_D(QQuickTextInput);
2507 if (d->selectByMouse != on) {
2508 d->selectByMouse = on;
2509 emit selectByMouseChanged(on);
2510 }
2511}
2512
2513/*!
2514 \qmlproperty enumeration QtQuick::TextInput::mouseSelectionMode
2515
2516 Specifies how text should be selected using a mouse.
2517
2518 \value TextInput.SelectCharacters (default) The selection is updated with individual characters.
2519 \value TextInput.SelectWords The selection is updated with whole words.
2520
2521 This property only applies when \l selectByMouse is true.
2522*/
2523
2524QQuickTextInput::SelectionMode QQuickTextInput::mouseSelectionMode() const
2525{
2526 Q_D(const QQuickTextInput);
2527 return d->mouseSelectionMode;
2528}
2529
2530void QQuickTextInput::setMouseSelectionMode(SelectionMode mode)
2531{
2532 Q_D(QQuickTextInput);
2533 if (d->mouseSelectionMode != mode) {
2534 d->mouseSelectionMode = mode;
2535 emit mouseSelectionModeChanged(mode);
2536 }
2537}
2538
2539/*!
2540 \qmlproperty bool QtQuick::TextInput::persistentSelection
2541
2542 Whether the TextInput should keep its selection when it loses active focus to another
2543 item in the scene. By default this is set to false;
2544*/
2545
2546bool QQuickTextInput::persistentSelection() const
2547{
2548 Q_D(const QQuickTextInput);
2549 return d->persistentSelection;
2550}
2551
2552void QQuickTextInput::setPersistentSelection(bool on)
2553{
2554 Q_D(QQuickTextInput);
2555 if (d->persistentSelection == on)
2556 return;
2557 d->persistentSelection = on;
2558 emit persistentSelectionChanged();
2559}
2560
2561/*!
2562 \qmlproperty bool QtQuick::TextInput::canPaste
2563 \readonly
2564
2565 Returns true if the TextInput is writable and the content of the clipboard is
2566 suitable for pasting into the TextInput.
2567*/
2568bool QQuickTextInput::canPaste() const
2569{
2570#if QT_CONFIG(clipboard)
2571 Q_D(const QQuickTextInput);
2572 if (!d->canPasteValid) {
2573 bool canPaste = false;
2574 if (!d->m_readOnly) {
2575 if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
2576 canPaste = mimeData->hasText() && !mimeData->text().isEmpty();
2577 }
2578 const_cast<QQuickTextInputPrivate *>(d)->canPaste = canPaste;
2579 const_cast<QQuickTextInputPrivate *>(d)->canPasteValid = true;
2580 }
2581 return d->canPaste;
2582#else
2583 return false;
2584#endif
2585}
2586
2587/*!
2588 \qmlproperty bool QtQuick::TextInput::canUndo
2589 \readonly
2590
2591 Returns true if the TextInput is writable and there are previous operations
2592 that can be undone.
2593*/
2594
2595bool QQuickTextInput::canUndo() const
2596{
2597 Q_D(const QQuickTextInput);
2598 return d->canUndo;
2599}
2600
2601/*!
2602 \qmlproperty bool QtQuick::TextInput::canRedo
2603 \readonly
2604
2605 Returns true if the TextInput is writable and there are \l {undo}{undone}
2606 operations that can be redone.
2607*/
2608
2609bool QQuickTextInput::canRedo() const
2610{
2611 Q_D(const QQuickTextInput);
2612 return d->canRedo;
2613}
2614
2615/*!
2616 \qmlproperty real QtQuick::TextInput::contentWidth
2617 \readonly
2618
2619 Returns the width of the text, including the width past the width
2620 which is covered due to insufficient wrapping if \l wrapMode is set.
2621*/
2622
2623qreal QQuickTextInput::contentWidth() const
2624{
2625 Q_D(const QQuickTextInput);
2626 return d->contentSize.width();
2627}
2628
2629/*!
2630 \qmlproperty real QtQuick::TextInput::contentHeight
2631 \readonly
2632
2633 Returns the height of the text, including the height past the height
2634 that is covered if the text does not fit within the set height.
2635*/
2636
2637qreal QQuickTextInput::contentHeight() const
2638{
2639 Q_D(const QQuickTextInput);
2640 return d->contentSize.height();
2641}
2642
2643void QQuickTextInput::moveCursorSelection(int position)
2644{
2645 Q_D(QQuickTextInput);
2646 d->moveCursor(position, true);
2647}
2648
2649/*!
2650 \qmlmethod void QtQuick::TextInput::moveCursorSelection(int position, SelectionMode mode)
2651
2652 Moves the cursor to \a position and updates the selection according to the optional \a mode
2653 parameter. (To only move the cursor, set the \l cursorPosition property.)
2654
2655 When this method is called it additionally sets either the
2656 selectionStart or the selectionEnd (whichever was at the previous cursor position)
2657 to the specified position. This allows you to easily extend and contract the selected
2658 text range.
2659
2660 The selection mode specifies whether the selection is updated on a per character or a per word
2661 basis. If not specified the selection mode will default to \c {TextInput.SelectCharacters}.
2662
2663 \value TextInput.SelectCharacters
2664 Sets either the selectionStart or selectionEnd (whichever was at the previous cursor position)
2665 to the specified position.
2666 \value TextInput.SelectWords
2667 Sets the selectionStart and selectionEnd to include all words between the specified position
2668 and the previous cursor position. Words partially in the range are included.
2669
2670 For example, take this sequence of calls:
2671
2672 \code
2673 cursorPosition = 5
2674 moveCursorSelection(9, TextInput.SelectCharacters)
2675 moveCursorSelection(7, TextInput.SelectCharacters)
2676 \endcode
2677
2678 This moves the cursor to position 5, extend the selection end from 5 to 9
2679 and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
2680 selected (the 6th and 7th characters).
2681
2682 The same sequence with TextInput.SelectWords will extend the selection start to a word boundary
2683 before or on position 5 and extend the selection end to a word boundary on or past position 9.
2684*/
2685void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
2686{
2687 Q_D(QQuickTextInput);
2688
2689 if (mode == SelectCharacters) {
2690 d->moveCursor(pos, true);
2691 } else if (pos != d->m_cursor) {
2692 const int cursor = d->m_cursor;
2693 int anchor;
2694 if (!d->hasSelectedText())
2695 anchor = d->m_cursor;
2696 else if (d->selectionStart() == d->m_cursor)
2697 anchor = d->selectionEnd();
2698 else
2699 anchor = d->selectionStart();
2700
2701 if (anchor < pos || (anchor == pos && cursor < pos)) {
2702 const QString text = this->text();
2703 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
2704 finder.setPosition(anchor);
2705
2706 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
2707 if (anchor < text.size() && (reasons == QTextBoundaryFinder::NotAtBoundary
2708 || (reasons & QTextBoundaryFinder::EndOfItem))) {
2709 finder.toPreviousBoundary();
2710 }
2711 anchor = finder.position() != -1 ? finder.position() : 0;
2712
2713 finder.setPosition(pos);
2714 if (pos > 0 && !finder.boundaryReasons())
2715 finder.toNextBoundary();
2716 const int cursor = finder.position() != -1 ? finder.position() : text.size();
2717
2718 d->setSelection(anchor, cursor - anchor);
2719 } else if (anchor > pos || (anchor == pos && cursor > pos)) {
2720 const QString text = this->text();
2721 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
2722 finder.setPosition(anchor);
2723
2724 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
2725 if (anchor > 0 && (reasons == QTextBoundaryFinder::NotAtBoundary
2726 || (reasons & QTextBoundaryFinder::StartOfItem))) {
2727 finder.toNextBoundary();
2728 }
2729 anchor = finder.position() != -1 ? finder.position() : text.size();
2730
2731 finder.setPosition(pos);
2732 if (pos < text.size() && !finder.boundaryReasons())
2733 finder.toPreviousBoundary();
2734 const int cursor = finder.position() != -1 ? finder.position() : 0;
2735
2736 d->setSelection(anchor, cursor - anchor);
2737 }
2738 }
2739}
2740
2741void QQuickTextInput::focusInEvent(QFocusEvent *event)
2742{
2743 Q_D(QQuickTextInput);
2744 d->handleFocusEvent(event);
2745 QQuickImplicitSizeItem::focusInEvent(event);
2746}
2747
2748void QQuickTextInputPrivate::handleFocusEvent(QFocusEvent *event)
2749{
2750 Q_Q(QQuickTextInput);
2751 bool focus = event->gotFocus();
2752 if (!m_readOnly) {
2753 q->setCursorVisible(focus);
2754 setBlinkingCursorEnabled(focus);
2755 }
2756 if (focus) {
2757 q->q_updateAlignment();
2758#if QT_CONFIG(im)
2759 if (focusOnPress && !m_readOnly)
2760 qGuiApp->inputMethod()->show();
2761 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
2762 q, SLOT(q_updateAlignment()));
2763#endif
2764 } else {
2765 if ((m_passwordEchoEditing || m_passwordEchoTimer.isActive())) {
2766 updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
2767 }
2768
2769 if (event->reason() != Qt::ActiveWindowFocusReason
2770 && event->reason() != Qt::PopupFocusReason
2771 && hasSelectedText()
2772 && !persistentSelection)
2773 deselect();
2774
2775 if (hasAcceptableInput(m_text) == AcceptableInput || fixup())
2776 emit q->editingFinished();
2777
2778#if QT_CONFIG(im)
2779 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
2780 q, SLOT(q_updateAlignment()));
2781#endif
2782 }
2783}
2784
2785void QQuickTextInput::focusOutEvent(QFocusEvent *event)
2786{
2787 Q_D(QQuickTextInput);
2788 d->handleFocusEvent(event);
2789 QQuickImplicitSizeItem::focusOutEvent(event);
2790}
2791
2792void QQuickTextInputPrivate::readOnlyChanged(bool isReadOnly)
2793{
2794 Q_UNUSED(isReadOnly);
2795#if QT_CONFIG(accessibility)
2796 if (QQuickAccessibleAttached *accessibleAttached =
2797 QQuickAccessibleAttached::attachedProperties(q_func()))
2798 accessibleAttached->set_readOnly(isReadOnly);
2799#endif
2800}
2801
2802void QQuickTextInputPrivate::echoModeChanged(QQuickTextInput::EchoMode echoMode)
2803{
2804#if QT_CONFIG(accessibility)
2805 if (!QAccessible::isActive())
2806 return;
2807
2808 if (QQuickAccessibleAttached *accessibleAttached =
2809 QQuickAccessibleAttached::attachedProperties(q_func()))
2810 accessibleAttached->set_passwordEdit((echoMode == QQuickTextInput::Password
2811 || echoMode == QQuickTextInput::PasswordEchoOnEdit)
2812 ? true
2813 : false);
2814#else
2815 Q_UNUSED(echoMode);
2816#endif
2817}
2818
2819#if QT_CONFIG(accessibility)
2820void QQuickTextInputPrivate::accessibilityActiveChanged(bool active)
2821{
2822 if (!active)
2823 return;
2824
2825 Q_Q(QQuickTextInput);
2826 QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
2827 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
2828 Q_ASSERT(accessibleAttached);
2829 accessibleAttached->setRole(effectiveAccessibleRole());
2830 accessibleAttached->set_readOnly(m_readOnly);
2831 accessibleAttached->set_passwordEdit((m_echoMode == QQuickTextInput::Password
2832 || m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
2833 ? true
2834 : false);
2835}
2836
2837QAccessible::Role QQuickTextInputPrivate::accessibleRole() const
2838{
2839 return QAccessible::EditableText;
2840}
2841#endif
2842
2843/*!
2844 \qmlproperty bool QtQuick::TextInput::inputMethodComposing
2845 \readonly
2846
2847 This property holds whether the TextInput has partial text input from an
2848 input method.
2849
2850 While it is composing an input method may rely on mouse or key events from
2851 the TextInput to edit or commit the partial text. This property can be
2852 used to determine when to disable events handlers that may interfere with
2853 the correct operation of an input method.
2854*/
2855bool QQuickTextInput::isInputMethodComposing() const
2856{
2857#if !QT_CONFIG(im)
2858 return false;
2859#else
2860 Q_D(const QQuickTextInput);
2861 return d->hasImState;
2862#endif
2863}
2864
2865QQuickTextInputPrivate::ExtraData::ExtraData()
2866 : padding(0)
2867 , topPadding(0)
2868 , leftPadding(0)
2869 , rightPadding(0)
2870 , bottomPadding(0)
2871 , explicitTopPadding(false)
2872 , explicitLeftPadding(false)
2873 , explicitRightPadding(false)
2874 , explicitBottomPadding(false)
2875 , implicitResize(true)
2876{
2877}
2878
2879void QQuickTextInputPrivate::init()
2880{
2881 Q_Q(QQuickTextInput);
2882#if QT_CONFIG(clipboard)
2883 if (QGuiApplication::clipboard()->supportsSelection())
2884 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2885 else
2886#endif
2887 q->setAcceptedMouseButtons(Qt::LeftButton);
2888
2889#if QT_CONFIG(im)
2890 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2891#endif
2892 q->setFlag(QQuickItem::ItemHasContents);
2893#if QT_CONFIG(clipboard)
2894 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()),
2895 q, QQuickTextInput, SLOT(q_canPasteChanged()));
2896#endif // clipboard
2897
2898 lastSelectionStart = 0;
2899 lastSelectionEnd = 0;
2900 determineHorizontalAlignment();
2901
2902 if (!qmlDisableDistanceField()) {
2903 QTextOption option = m_textLayout.textOption();
2904 option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
2905 m_textLayout.setTextOption(option);
2906 }
2907
2908 m_inputControl = new QInputControl(QInputControl::LineEdit, q);
2909 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Fixed);
2910
2911 QObjectPrivate::connect(q, &QQuickTextInput::readOnlyChanged, this,
2912 &QQuickTextInputPrivate::readOnlyChanged);
2913 QObjectPrivate::connect(q, &QQuickTextInput::echoModeChanged, this,
2914 &QQuickTextInputPrivate::echoModeChanged);
2915}
2916
2917void QQuickTextInputPrivate::cancelInput()
2918{
2919#if QT_CONFIG(im)
2920 Q_Q(QQuickTextInput);
2921 if (!m_readOnly && q->hasActiveFocus() && qGuiApp)
2922 cancelPreedit();
2923#endif // im
2924}
2925
2926void QQuickTextInput::updateCursorRectangle(bool scroll)
2927{
2928 Q_D(QQuickTextInput);
2929 if (!isComponentComplete())
2930 return;
2931
2932 if (scroll) {
2933 d->updateHorizontalScroll();
2934 d->updateVerticalScroll();
2935 }
2936 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
2937 polish();
2938 update();
2939 emit cursorRectangleChanged();
2940 if (d->cursorItem) {
2941 QRectF r = cursorRectangle();
2942 d->cursorItem->setPosition(r.topLeft());
2943 d->cursorItem->setHeight(r.height());
2944 }
2945#if QT_CONFIG(im)
2946 updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
2947#endif
2948}
2949
2950void QQuickTextInput::selectionChanged()
2951{
2952 Q_D(QQuickTextInput);
2953 d->textLayoutDirty = true; //TODO: Only update rect in selection
2954 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
2955 polish();
2956 update();
2957 emit selectedTextChanged();
2958
2959 if (d->lastSelectionStart != d->selectionStart()) {
2960 d->lastSelectionStart = d->selectionStart();
2961 if (d->lastSelectionStart == -1)
2962 d->lastSelectionStart = d->m_cursor;
2963 emit selectionStartChanged();
2964 }
2965 if (d->lastSelectionEnd != d->selectionEnd()) {
2966 d->lastSelectionEnd = d->selectionEnd();
2967 if (d->lastSelectionEnd == -1)
2968 d->lastSelectionEnd = d->m_cursor;
2969 emit selectionEndChanged();
2970 }
2971}
2972
2973QRectF QQuickTextInput::boundingRect() const
2974{
2975 Q_D(const QQuickTextInput);
2976
2977 int cursorWidth = d->cursorItem ? 0 : 1;
2978
2979 qreal hscroll = d->hscroll;
2980 if (!d->autoScroll || d->contentSize.width() < width())
2981 hscroll -= QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign());
2982
2983 // Could include font max left/right bearings to either side of rectangle.
2984 QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height());
2985 r.setRight(r.right() + cursorWidth);
2986 return r;
2987}
2988
2989QRectF QQuickTextInput::clipRect() const
2990{
2991 Q_D(const QQuickTextInput);
2992
2993 int cursorWidth = d->cursorItem ? d->cursorItem->width() : 1;
2994
2995 // Could include font max left/right bearings to either side of rectangle.
2996 QRectF r = QQuickImplicitSizeItem::clipRect();
2997 r.setRight(r.right() + cursorWidth);
2998 return r;
2999}
3000
3001void QQuickTextInput::q_canPasteChanged()
3002{
3003 Q_D(QQuickTextInput);
3004 bool old = d->canPaste;
3005#if QT_CONFIG(clipboard)
3006 bool canPaste = false;
3007 if (!d->m_readOnly) {
3008 if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
3009 canPaste = mimeData->hasText() && !mimeData->text().isEmpty();
3010 }
3011 d->canPaste = canPaste;
3012#endif
3013
3014 bool changed = d->canPaste != old || !d->canPasteValid;
3015 d->canPasteValid = true;
3016 if (changed)
3017 emit canPasteChanged();
3018
3019}
3020
3021void QQuickTextInput::q_updateAlignment()
3022{
3023 Q_D(QQuickTextInput);
3024 if (d->determineHorizontalAlignment()) {
3025 d->updateLayout();
3026 updateCursorRectangle();
3027 }
3028}
3029
3030/*!
3031 \internal
3032
3033 Updates the display text based of the current edit text
3034 If the text has changed will emit displayTextChanged()
3035*/
3036void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
3037{
3038 QString orig = m_textLayout.text();
3039 QString str;
3040 if (m_echoMode == QQuickTextInput::NoEcho)
3041 str = QString::fromLatin1("");
3042 else
3043 str = m_text;
3044
3045 if (m_echoMode == QQuickTextInput::Password) {
3046 str.fill(m_passwordCharacter);
3047 if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.size()) {
3048 int cursor = m_cursor - 1;
3049 QChar uc = m_text.at(cursor);
3050 str[cursor] = uc;
3051 if (cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
3052 // second half of a surrogate, check if we have the first half as well,
3053 // if yes restore both at once
3054 uc = m_text.at(cursor - 1);
3055 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00)
3056 str[cursor - 1] = uc;
3057 }
3058 }
3059 } else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
3060 str.fill(m_passwordCharacter);
3061 }
3062
3063 // replace certain non-printable characters with spaces (to avoid
3064 // drawing boxes when using fonts that don't have glyphs for such
3065 // characters)
3066 QChar* uc = str.data();
3067 for (int i = 0; i < str.size(); ++i) {
3068 if (uc[i] == QChar::LineSeparator
3069 || uc[i] == QChar::ParagraphSeparator
3070 || uc[i] == QChar::ObjectReplacementCharacter)
3071 uc[i] = QChar(0x0020);
3072 }
3073
3074 if (str != orig || forceUpdate) {
3075 m_textLayout.setText(str);
3076 updateLayout(); // polish?
3077 emit q_func()->displayTextChanged();
3078 }
3079}
3080
3081qreal QQuickTextInputPrivate::calculateImplicitWidthForText(const QString &text) const
3082{
3083 Q_Q(const QQuickTextInput);
3084 QTextLayout layout(text);
3085
3086 QTextOption option = m_textLayout.textOption();
3087 option.setTextDirection(m_layoutDirection);
3088 option.setFlags(QTextOption::IncludeTrailingSpaces);
3089 option.setWrapMode(QTextOption::WrapMode(wrapMode));
3090 option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
3091 layout.setTextOption(option);
3092 layout.setFont(font);
3093#if QT_CONFIG(im)
3094 layout.setPreeditArea(m_textLayout.preeditAreaPosition(), m_textLayout.preeditAreaText());
3095#endif
3096 layout.beginLayout();
3097
3098 QTextLine line = layout.createLine();
3099 line.setLineWidth(qreal(INT_MAX));
3100 const qreal theImplicitWidth = qCeil(line.naturalTextWidth()) + q->leftPadding() + q->rightPadding();
3101
3102 layout.endLayout();
3103 return theImplicitWidth;
3104}
3105
3106qreal QQuickTextInputPrivate::getImplicitWidth() const
3107{
3108 Q_Q(const QQuickTextInput);
3109 if (!requireImplicitWidth) {
3110 QQuickTextInputPrivate *d = const_cast<QQuickTextInputPrivate *>(this);
3111 d->requireImplicitWidth = true;
3112
3113 if (q->isComponentComplete())
3114 d->implicitWidth = calculateImplicitWidthForText(m_text);
3115 }
3116 return implicitWidth;
3117}
3118
3119void QQuickTextInputPrivate::setTopPadding(qreal value, bool reset)
3120{
3121 Q_Q(QQuickTextInput);
3122 qreal oldPadding = q->topPadding();
3123 if (!reset || extra.isAllocated()) {
3124 extra.value().topPadding = value;
3125 extra.value().explicitTopPadding = !reset;
3126 }
3127 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
3128 updateLayout();
3129 q->updateCursorRectangle();
3130 emit q->topPaddingChanged();
3131 }
3132}
3133
3134void QQuickTextInputPrivate::setLeftPadding(qreal value, bool reset)
3135{
3136 Q_Q(QQuickTextInput);
3137 qreal oldPadding = q->leftPadding();
3138 if (!reset || extra.isAllocated()) {
3139 extra.value().leftPadding = value;
3140 extra.value().explicitLeftPadding = !reset;
3141 }
3142 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
3143 updateLayout();
3144 q->updateCursorRectangle();
3145 emit q->leftPaddingChanged();
3146 }
3147}
3148
3149void QQuickTextInputPrivate::setRightPadding(qreal value, bool reset)
3150{
3151 Q_Q(QQuickTextInput);
3152 qreal oldPadding = q->rightPadding();
3153 if (!reset || extra.isAllocated()) {
3154 extra.value().rightPadding = value;
3155 extra.value().explicitRightPadding = !reset;
3156 }
3157 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
3158 updateLayout();
3159 q->updateCursorRectangle();
3160 emit q->rightPaddingChanged();
3161 }
3162}
3163
3164void QQuickTextInputPrivate::setBottomPadding(qreal value, bool reset)
3165{
3166 Q_Q(QQuickTextInput);
3167 qreal oldPadding = q->bottomPadding();
3168 if (!reset || extra.isAllocated()) {
3169 extra.value().bottomPadding = value;
3170 extra.value().explicitBottomPadding = !reset;
3171 }
3172 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
3173 updateLayout();
3174 q->updateCursorRectangle();
3175 emit q->bottomPaddingChanged();
3176 }
3177}
3178
3179bool QQuickTextInputPrivate::isImplicitResizeEnabled() const
3180{
3181 return !extra.isAllocated() || extra->implicitResize;
3182}
3183
3184void QQuickTextInputPrivate::setImplicitResizeEnabled(bool enabled)
3185{
3186 if (!enabled)
3187 extra.value().implicitResize = false;
3188 else if (extra.isAllocated())
3189 extra->implicitResize = true;
3190}
3191
3192void QQuickTextInputPrivate::updateLayout()
3193{
3194 Q_Q(QQuickTextInput);
3195
3196 if (!q->isComponentComplete())
3197 return;
3198
3199
3200 QTextOption option = m_textLayout.textOption();
3201 option.setTextDirection(layoutDirection());
3202 option.setWrapMode(QTextOption::WrapMode(wrapMode));
3203 option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
3204 if (!qmlDisableDistanceField())
3205 option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
3206
3207 m_textLayout.setTextOption(option);
3208 m_textLayout.setFont(font);
3209
3210 m_textLayout.beginLayout();
3211
3212 QTextLine line = m_textLayout.createLine();
3213 if (requireImplicitWidth) {
3214 line.setLineWidth(qreal(INT_MAX));
3215 const bool wasInLayout = inLayout;
3216 inLayout = true;
3217 if (isImplicitResizeEnabled())
3218 q->setImplicitWidth(qCeil(line.naturalTextWidth()) + q->leftPadding() + q->rightPadding());
3219 inLayout = wasInLayout;
3220 if (inLayout) // probably the result of a binding loop, but by letting it
3221 return; // get this far we'll get a warning to that effect.
3222 }
3223 qreal lineWidth = q->widthValid() || !isImplicitResizeEnabled() ? q->width() - q->leftPadding() - q->rightPadding() : qreal(INT_MAX);
3224 qreal height = 0;
3225 qreal width = 0;
3226 do {
3227 line.setLineWidth(lineWidth);
3228 line.setPosition(QPointF(0, height));
3229
3230 height += line.height();
3231 width = qMax(width, line.naturalTextWidth());
3232
3233 line = m_textLayout.createLine();
3234 } while (line.isValid());
3235 m_textLayout.endLayout();
3236
3237 option.setWrapMode(QTextOption::NoWrap);
3238 m_textLayout.setTextOption(option);
3239
3240 textLayoutDirty = true;
3241
3242 const QSizeF previousSize = contentSize;
3243 contentSize = QSizeF(width, height);
3244
3245 updateType = UpdatePaintNode;
3246 q->polish();
3247 q->update();
3248
3249 if (isImplicitResizeEnabled()) {
3250 if (!requireImplicitWidth && !q->widthValid())
3251 q->setImplicitSize(width + q->leftPadding() + q->rightPadding(), height + q->topPadding() + q->bottomPadding());
3252 else
3253 q->setImplicitHeight(height + q->topPadding() + q->bottomPadding());
3254 }
3255
3256 updateBaselineOffset();
3257
3258 if (previousSize != contentSize)
3259 emit q->contentSizeChanged();
3260}
3261
3262/*!
3263 \internal
3264 \brief QQuickTextInputPrivate::updateBaselineOffset
3265
3266 Assumes contentSize.height() is already calculated.
3267 */
3268void QQuickTextInputPrivate::updateBaselineOffset()
3269{
3270 Q_Q(QQuickTextInput);
3271 if (!q->isComponentComplete())
3272 return;
3273 QFontMetricsF fm(font);
3274 qreal yoff = 0;
3275 if (q->heightValid()) {
3276 const qreal surplusHeight = q->height() - contentSize.height() - q->topPadding() - q->bottomPadding();
3277 if (vAlign == QQuickTextInput::AlignBottom)
3278 yoff = surplusHeight;
3279 else if (vAlign == QQuickTextInput::AlignVCenter)
3280 yoff = surplusHeight/2;
3281 }
3282 qreal ascent;
3283 if (m_textLayout.lineCount() > 0) {
3284 QTextLine line = m_textLayout.lineAt(0);
3285 ascent = line.y() + line.ascent();
3286 } else {
3287 ascent = fm.ascent();
3288 }
3289 q->setBaselineOffset(ascent + yoff + q->topPadding());
3290}
3291
3292#if QT_CONFIG(clipboard)
3293/*!
3294 \internal
3295
3296 Copies the currently selected text into the clipboard using the given
3297 \a mode.
3298
3299 \note If the echo mode is set to a mode other than Normal then copy
3300 will not work. This is to prevent using copy as a method of bypassing
3301 password features of the line control.
3302*/
3303void QQuickTextInputPrivate::copy(QClipboard::Mode mode) const
3304{
3305 QString t = selectedText();
3306 if (!t.isEmpty() && m_echoMode == QQuickTextInput::Normal) {
3307 QGuiApplication::clipboard()->setText(t, mode);
3308 }
3309}
3310
3311/*!
3312 \internal
3313
3314 Inserts the text stored in the application clipboard into the line
3315 control.
3316
3317 \sa insert()
3318*/
3319void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode)
3320{
3321 QString clip = QGuiApplication::clipboard()->text(clipboardMode);
3322 if (!clip.isEmpty() || hasSelectedText()) {
3323 separate(); //make it a separate undo/redo command
3324 insert(clip);
3325 separate();
3326 }
3327}
3328
3329#endif // clipboard
3330
3331#if QT_CONFIG(im)
3332/*!
3333 \internal
3334*/
3335void QQuickTextInputPrivate::commitPreedit()
3336{
3337 Q_Q(QQuickTextInput);
3338
3339 if (!hasImState)
3340 return;
3341
3342 QGuiApplication::inputMethod()->commit();
3343
3344 if (!hasImState)
3345 return;
3346
3347 QInputMethodEvent ev;
3348 QCoreApplication::sendEvent(q, &ev);
3349}
3350
3351void QQuickTextInputPrivate::cancelPreedit()
3352{
3353 Q_Q(QQuickTextInput);
3354
3355 if (!hasImState)
3356 return;
3357
3358 QGuiApplication::inputMethod()->reset();
3359
3360 QInputMethodEvent ev;
3361 QCoreApplication::sendEvent(q, &ev);
3362}
3363#endif // im
3364
3365/*!
3366 \internal
3367
3368 Handles the behavior for the backspace key or function.
3369 Removes the current selection if there is a selection, otherwise
3370 removes the character prior to the cursor position.
3371
3372 \sa del()
3373*/
3374void QQuickTextInputPrivate::backspace()
3375{
3376 int priorState = m_undoState;
3377 if (separateSelection()) {
3378 removeSelectedText();
3379 } else if (m_cursor) {
3380 --m_cursor;
3381 if (m_maskData)
3382 m_cursor = prevMaskBlank(m_cursor);
3383 QChar uc = m_text.at(m_cursor);
3384 if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
3385 // second half of a surrogate, check if we have the first half as well,
3386 // if yes delete both at once
3387 uc = m_text.at(m_cursor - 1);
3388 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
3389 internalDelete(true);
3390 --m_cursor;
3391 }
3392 }
3393 internalDelete(true);
3394 }
3395 finishChange(priorState);
3396}
3397
3398/*!
3399 \internal
3400
3401 Handles the behavior for the delete key or function.
3402 Removes the current selection if there is a selection, otherwise
3403 removes the character after the cursor position.
3404
3405 \sa del()
3406*/
3407void QQuickTextInputPrivate::del()
3408{
3409 int priorState = m_undoState;
3410 if (separateSelection()) {
3411 removeSelectedText();
3412 } else {
3413 int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
3414 while (n--)
3415 internalDelete();
3416 }
3417 finishChange(priorState);
3418}
3419
3420/*!
3421 \internal
3422
3423 Inserts the given \a newText at the current cursor position.
3424 If there is any selected text it is removed prior to insertion of
3425 the new text.
3426*/
3427void QQuickTextInputPrivate::insert(const QString &newText)
3428{
3429 int priorState = m_undoState;
3430 if (separateSelection())
3431 removeSelectedText();
3432 internalInsert(newText);
3433 finishChange(priorState);
3434}
3435
3436/*!
3437 \internal
3438
3439 Clears the line control text.
3440*/
3441void QQuickTextInputPrivate::clear()
3442{
3443 int priorState = m_undoState;
3444 separateSelection();
3445 m_selstart = 0;
3446 m_selend = m_text.size();
3447 removeSelectedText();
3448 separate();
3449 finishChange(priorState, /*update*/false, /*edited*/false);
3450}
3451
3452/*!
3453 \internal
3454
3455 Sets \a length characters from the given \a start position as selected.
3456 The given \a start position must be within the current text for
3457 the line control. If \a length characters cannot be selected, then
3458 the selection will extend to the end of the current text.
3459*/
3460void QQuickTextInputPrivate::setSelection(int start, int length)
3461{
3462 Q_Q(QQuickTextInput);
3463#if QT_CONFIG(im)
3464 commitPreedit();
3465#endif
3466
3467 if (start < 0 || start > m_text.size()) {
3468 qWarning("QQuickTextInputPrivate::setSelection: Invalid start position");
3469 return;
3470 }
3471
3472 if (length > 0) {
3473 if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
3474 return;
3475 m_selstart = start;
3476 m_selend = qMin(start + length, m_text.size());
3477 m_cursor = m_selend;
3478 } else if (length < 0) {
3479 if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
3480 return;
3481 m_selstart = qMax(start + length, 0);
3482 m_selend = start;
3483 m_cursor = m_selstart;
3484 } else if (m_selstart != m_selend) {
3485 m_selstart = 0;
3486 m_selend = 0;
3487 m_cursor = start;
3488 } else {
3489 m_cursor = start;
3490 emitCursorPositionChanged();
3491 return;
3492 }
3493 emit q->selectionChanged();
3494 emitCursorPositionChanged();
3495#if QT_CONFIG(im)
3496 q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImCursorPosition | Qt::ImAnchorPosition
3497 | Qt::ImCurrentSelection);
3498#endif
3499}
3500
3501/*!
3502 \internal
3503
3504 Sets the password echo editing to \a editing. If password echo editing
3505 is true, then the text of the password is displayed even if the echo
3506 mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing
3507 does not affect other echo modes.
3508*/
3509void QQuickTextInputPrivate::updatePasswordEchoEditing(bool editing)
3510{
3511 cancelPasswordEchoTimer();
3512 m_passwordEchoEditing = editing;
3513 updateDisplayText();
3514}
3515
3516/*!
3517 \internal
3518
3519 Fixes the current text so that it is valid given any set validators.
3520
3521 Returns true if the text was changed. Otherwise returns false.
3522*/
3523bool QQuickTextInputPrivate::fixup() // this function assumes that validate currently returns != Acceptable
3524{
3525#if QT_CONFIG(validator)
3526 if (m_validator) {
3527 QString textCopy = m_text;
3528 int cursorCopy = m_cursor;
3529 m_validator->fixup(textCopy);
3530 if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
3531 if (textCopy != m_text || cursorCopy != m_cursor)
3532 internalSetText(textCopy, cursorCopy);
3533 return true;
3534 }
3535 }
3536#endif
3537 return false;
3538}
3539
3540/*!
3541 \internal
3542
3543 Moves the cursor to the given position \a pos. If \a mark is true will
3544 adjust the currently selected text.
3545*/
3546void QQuickTextInputPrivate::moveCursor(int pos, bool mark)
3547{
3548 Q_Q(QQuickTextInput);
3549#if QT_CONFIG(im)
3550 commitPreedit();
3551#endif
3552
3553 if (pos != m_cursor) {
3554 separate();
3555 if (m_maskData)
3556 pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
3557 }
3558 if (mark) {
3559 int anchor;
3560 if (m_selend > m_selstart && m_cursor == m_selstart)
3561 anchor = m_selend;
3562 else if (m_selend > m_selstart && m_cursor == m_selend)
3563 anchor = m_selstart;
3564 else
3565 anchor = m_cursor;
3566 m_selstart = qMin(anchor, pos);
3567 m_selend = qMax(anchor, pos);
3568 } else {
3569 internalDeselect();
3570 }
3571 m_cursor = pos;
3572 if (mark || m_selDirty) {
3573 m_selDirty = false;
3574 emit q->selectionChanged();
3575 }
3576 emitCursorPositionChanged();
3577#if QT_CONFIG(im)
3578 q->updateInputMethod();
3579#endif
3580}
3581
3582#if QT_CONFIG(im)
3583/*!
3584 \internal
3585
3586 Applies the given input method event \a event to the text of the line
3587 control
3588*/
3589void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
3590{
3591 Q_Q(QQuickTextInput);
3592
3593 int priorState = -1;
3594 bool isGettingInput = !event->commitString().isEmpty()
3595 || event->preeditString() != preeditAreaText()
3596 || event->replacementLength() > 0;
3597 bool cursorPositionChanged = false;
3598 bool selectionChange = false;
3599 m_preeditDirty = event->preeditString() != preeditAreaText();
3600
3601 if (isGettingInput) {
3602 // If any text is being input, remove selected text.
3603 priorState = m_undoState;
3604 separateSelection();
3605 if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
3606 updatePasswordEchoEditing(true);
3607 m_selstart = 0;
3608 m_selend = m_text.size();
3609 }
3610 removeSelectedText();
3611 }
3612
3613 int c = m_cursor; // cursor position after insertion of commit string
3614 if (event->replacementStart() <= 0)
3615 c += event->commitString().size() - qMin(-event->replacementStart(), event->replacementLength());
3616
3617 int cursorInsertPos = m_cursor + event->replacementStart();
3618 if (cursorInsertPos < 0)
3619 cursorInsertPos = 0;
3620
3621 // insert commit string
3622 if (event->replacementLength()) {
3623 m_selstart = cursorInsertPos;
3624 m_selend = m_selstart + event->replacementLength();
3625 m_selend = qMin(m_selend, m_text.size());
3626 removeSelectedText();
3627 }
3628 m_cursor = cursorInsertPos;
3629
3630 if (!event->commitString().isEmpty()) {
3631 internalInsert(event->commitString());
3632 cursorPositionChanged = true;
3633 } else {
3634 m_cursor = qBound(0, c, m_text.size());
3635 }
3636
3637 for (int i = 0; i < event->attributes().size(); ++i) {
3638 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
3639 if (a.type == QInputMethodEvent::Selection) {
3640 // If we already called internalInsert(), the cursor position will
3641 // already be adjusted correctly. The attribute.start does
3642 // not seem to take the mask into account, so it will reset the cursor
3643 // to an invalid position in such case. However, when the input mask
3644 // is not active, we must apply the cursor position regardless of the
3645 // commit string.
3646 if (!cursorPositionChanged || !m_maskData)
3647 m_cursor = qBound(0, a.start + a.length, m_text.size());
3648 if (a.length) {
3649 m_selstart = qMax(0, qMin(a.start, m_text.size()));
3650 m_selend = m_cursor;
3651 if (m_selend < m_selstart) {
3652 qSwap(m_selstart, m_selend);
3653 }
3654 selectionChange = true;
3655 } else {
3656 selectionChange = m_selstart != m_selend;
3657 m_selstart = m_selend = 0;
3658 }
3659 cursorPositionChanged = true;
3660 }
3661 }
3662 QString oldPreeditString = m_textLayout.preeditAreaText();
3663 m_textLayout.setPreeditArea(m_cursor, event->preeditString());
3664 if (oldPreeditString != m_textLayout.preeditAreaText()) {
3665 emit q->preeditTextChanged();
3666 if (!event->preeditString().isEmpty() && m_undoPreeditState == -1)
3667 // Pre-edit text started. Remember state for undo purpose.
3668 m_undoPreeditState = priorState;
3669 }
3670 const int oldPreeditCursor = m_preeditCursor;
3671 m_preeditCursor = event->preeditString().size();
3672 hasImState = !event->preeditString().isEmpty();
3673 bool cursorVisible = true;
3674 QList<QTextLayout::FormatRange> formats;
3675 for (int i = 0; i < event->attributes().size(); ++i) {
3676 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
3677 if (a.type == QInputMethodEvent::Cursor) {
3678 hasImState = true;
3679 m_preeditCursor = a.start;
3680 cursorVisible = a.length != 0;
3681 } else if (a.type == QInputMethodEvent::TextFormat) {
3682 hasImState = true;
3683 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
3684 if (f.isValid()) {
3685 QTextLayout::FormatRange o;
3686 o.start = a.start + m_cursor;
3687 o.length = a.length;
3688 o.format = f;
3689 formats.append(o);
3690 }
3691 }
3692 }
3693 m_textLayout.setFormats(formats);
3694
3695 // Set cursor visible state. Do this before updating the text,
3696 // since user code connected to onTextChanged may set a different
3697 // cursor visible state (for instance by setting the focus), which
3698 // we don't want to overwrite.
3699 q->setCursorVisible(cursorVisible);
3700
3701 updateDisplayText(/*force*/ true);
3702 if (cursorPositionChanged && emitCursorPositionChanged())
3703 q->updateInputMethod(Qt::ImCursorPosition | Qt::ImAnchorPosition);
3704 else if (m_preeditCursor != oldPreeditCursor || isGettingInput)
3705 q->updateCursorRectangle();
3706
3707 if (isGettingInput)
3708 finishChange(priorState);
3709
3710 if (selectionChange) {
3711 emit q->selectionChanged();
3712 q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle
3713 | Qt::ImCurrentSelection);
3714 }
3715
3716 // Empty pre-edit text handled. Clean m_undoPreeditState
3717 if (event->preeditString().isEmpty())
3718 m_undoPreeditState = -1;
3719
3720}
3721#endif // im
3722
3723/*!
3724 \internal
3725
3726 Sets the selection to cover the word at the given cursor position.
3727 The word boundaries are defined by the behavior of QTextLayout::SkipWords
3728 cursor mode.
3729*/
3730void QQuickTextInputPrivate::selectWordAtPos(int cursor)
3731{
3732 int next = cursor + 1;
3733 if (next > end())
3734 --next;
3735 int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords);
3736 moveCursor(c, false);
3737 // ## text layout should support end of words.
3738 int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
3739 while (end > cursor && m_text.at(end - 1).isSpace())
3740 --end;
3741 moveCursor(end, true);
3742}
3743
3744/*!
3745 \internal
3746
3747 Completes a change to the line control text. If the change is not valid
3748 will undo the line control state back to the given \a validateFromState.
3749
3750 If \a edited is true and the change is valid, will emit textEdited() in
3751 addition to textChanged(). Otherwise only emits textChanged() on a valid
3752 change.
3753
3754 The \a update value is currently unused.
3755*/
3756bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bool edited)
3757{
3758 Q_Q(QQuickTextInput);
3759
3760 Q_UNUSED(update);
3761#if QT_CONFIG(im)
3762 bool inputMethodAttributesChanged = m_textDirty || m_selDirty;
3763#endif
3764 bool alignmentChanged = false;
3765 bool textChanged = false;
3766
3767 if (m_textDirty) {
3768 // do validation
3769 bool wasValidInput = m_validInput;
3770 bool wasAcceptable = m_acceptableInput;
3771 m_validInput = true;
3772 m_acceptableInput = true;
3773#if QT_CONFIG(validator)
3774 if (m_validator) {
3775 QString textCopy = m_text;
3776 if (m_maskData)
3777 textCopy = maskString(0, m_text, true);
3778 int cursorCopy = m_cursor;
3779 QValidator::State state = m_validator->validate(textCopy, cursorCopy);
3780 if (m_maskData)
3781 textCopy = m_text;
3782 m_validInput = state != QValidator::Invalid;
3783 m_acceptableInput = state == QValidator::Acceptable;
3784 if (m_validInput && !m_maskData) {
3785 if (m_text != textCopy) {
3786 internalSetText(textCopy, cursorCopy);
3787 return true;
3788 }
3789 m_cursor = cursorCopy;
3790 }
3791 }
3792#endif
3793 if (m_maskData)
3794 checkIsValid();
3795
3796#if QT_CONFIG(im)
3797 // If we were during pre-edit, validateFromState should point to the state before pre-edit
3798 // has been started. Choose the correct oldest remembered state
3799 if (m_undoPreeditState >= 0 && (m_undoPreeditState < validateFromState || validateFromState < 0))
3800 validateFromState = m_undoPreeditState;
3801#endif
3802 if (validateFromState >= 0 && wasValidInput && !m_validInput) {
3803 if (m_transactions.size())
3804 return false;
3805 internalUndo(validateFromState);
3806 m_history.resize(m_undoState);
3807 m_validInput = true;
3808 m_acceptableInput = wasAcceptable;
3809 m_textDirty = false;
3810 }
3811
3812 if (m_textDirty) {
3813 textChanged = true;
3814 m_textDirty = false;
3815#if QT_CONFIG(im)
3816 m_preeditDirty = false;
3817#endif
3818 alignmentChanged = determineHorizontalAlignment();
3819 if (edited)
3820 emit q->textEdited();
3821 emit q->textChanged();
3822 }
3823
3824 updateDisplayText(alignmentChanged);
3825
3826 if (m_acceptableInput != wasAcceptable)
3827 emit q->acceptableInputChanged();
3828 }
3829#if QT_CONFIG(im)
3830 if (m_preeditDirty) {
3831 m_preeditDirty = false;
3832 if (determineHorizontalAlignment()) {
3833 alignmentChanged = true;
3834 updateLayout();
3835 }
3836 }
3837#endif
3838
3839 if (m_selDirty) {
3840 m_selDirty = false;
3841 emit q->selectionChanged();
3842 }
3843
3844#if QT_CONFIG(im)
3845 inputMethodAttributesChanged |= (m_cursor != m_lastCursorPos);
3846 if (inputMethodAttributesChanged)
3847 q->updateInputMethod();
3848#endif
3849 emitUndoRedoChanged();
3850
3851 if (!emitCursorPositionChanged() && (alignmentChanged || textChanged))
3852 q->updateCursorRectangle();
3853
3854 return true;
3855}
3856
3857/*!
3858 \internal
3859
3860 An internal function for setting the text of the line control.
3861*/
3862void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool edited)
3863{
3864 internalDeselect();
3865 QString oldText = m_text;
3866 if (m_maskData) {
3867 m_text = maskString(0, txt, true);
3868 m_text += clearString(m_text.size(), m_maxLength - m_text.size());
3869 } else {
3870 m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
3871 }
3872 m_history.clear();
3873 m_undoState = 0;
3874#if QT_CONFIG(im)
3875 m_undoPreeditState = -1;
3876#endif
3877 m_cursor = (pos < 0 || pos > m_text.size()) ? m_text.size() : pos;
3878 m_textDirty = (oldText != m_text);
3879
3880 bool changed = finishChange(-1, true, edited);
3881#if !QT_CONFIG(accessibility)
3882 Q_UNUSED(changed);
3883#else
3884 Q_Q(QQuickTextInput);
3885 if (changed && QAccessible::isActive()) {
3886 if (QObject *acc = QQuickAccessibleAttached::findAccessible(q, QAccessible::EditableText)) {
3887 QAccessibleTextUpdateEvent ev(acc, 0, oldText, m_text);
3888 QAccessible::updateAccessibility(&ev);
3889 }
3890 }
3891#endif
3892}
3893
3894
3895/*!
3896 \internal
3897
3898 Adds the given \a command to the undo history
3899 of the line control. Does not apply the command.
3900*/
3901void QQuickTextInputPrivate::addCommand(const Command &cmd)
3902{
3903 if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) {
3904 m_history.resize(m_undoState + 2);
3905 m_history[m_undoState++] = Command(Separator, m_cursor, u'\0', m_selstart, m_selend);
3906 } else {
3907 m_history.resize(m_undoState + 1);
3908 }
3909 m_separator = false;
3910 m_history[m_undoState++] = cmd;
3911}
3912
3913/*!
3914 \internal
3915
3916 Inserts the given string \a s into the line
3917 control.
3918
3919 Also adds the appropriate commands into the undo history.
3920 This function does not call finishChange(), and may leave the text
3921 in an invalid state.
3922*/
3923void QQuickTextInputPrivate::internalInsert(const QString &s)
3924{
3925 Q_Q(QQuickTextInput);
3926 if (m_echoMode == QQuickTextInput::Password) {
3927 if (m_passwordMaskDelay > 0)
3928 m_passwordEchoTimer.start(m_passwordMaskDelay, q);
3929 }
3930 Q_ASSERT(!hasSelectedText()); // insert(), processInputMethodEvent() call removeSelectedText() first.
3931 if (m_maskData) {
3932 QString ms = maskString(m_cursor, s);
3933 for (int i = 0; i < ms.size(); ++i) {
3934 addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
3935 addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
3936 }
3937 m_text.replace(m_cursor, ms.size(), ms);
3938 m_cursor += ms.size();
3939 m_cursor = nextMaskBlank(m_cursor);
3940 m_textDirty = true;
3941 } else {
3942 int remaining = m_maxLength - m_text.size();
3943 if (remaining != 0) {
3944 const QStringView remainingStr = QStringView{s}.left(remaining);
3945 m_text.insert(m_cursor, remainingStr);
3946 for (auto e : remainingStr)
3947 addCommand(Command(Insert, m_cursor++, e, -1, -1));
3948 m_textDirty = true;
3949 }
3950 }
3951}
3952
3953/*!
3954 \internal
3955
3956 deletes a single character from the current text. If \a wasBackspace,
3957 the character prior to the cursor is removed. Otherwise the character
3958 after the cursor is removed.
3959
3960 Also adds the appropriate commands into the undo history.
3961 This function does not call finishChange(), and may leave the text
3962 in an invalid state.
3963*/
3964void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
3965{
3966 if (m_cursor < m_text.size()) {
3967 cancelPasswordEchoTimer();
3968 Q_ASSERT(!hasSelectedText()); // del(), backspace() call removeSelectedText() first.
3969 addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
3970 m_cursor, m_text.at(m_cursor), -1, -1));
3971 if (m_maskData) {
3972 m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
3973 addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
3974 } else {
3975 m_text.remove(m_cursor, 1);
3976 }
3977 m_textDirty = true;
3978 }
3979}
3980
3981/*!
3982 \internal
3983
3984 removes the currently selected text from the line control.
3985
3986 Also adds the appropriate commands into the undo history.
3987 This function does not call finishChange(), and may leave the text
3988 in an invalid state.
3989*/
3990void QQuickTextInputPrivate::removeSelectedText()
3991{
3992 if (m_selstart < m_selend && m_selend <= m_text.size()) {
3993 cancelPasswordEchoTimer();
3994 int i ;
3995 if (m_selstart <= m_cursor && m_cursor < m_selend) {
3996 // cursor is within the selection. Split up the commands
3997 // to be able to restore the correct cursor position
3998 for (i = m_cursor; i >= m_selstart; --i)
3999 addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
4000 for (i = m_selend - 1; i > m_cursor; --i)
4001 addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
4002 } else {
4003 for (i = m_selend-1; i >= m_selstart; --i)
4004 addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
4005 }
4006 if (m_maskData) {
4007 m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
4008 for (int i = 0; i < m_selend - m_selstart; ++i)
4009 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
4010 } else {
4011 m_text.remove(m_selstart, m_selend - m_selstart);
4012 }
4013 if (m_cursor > m_selstart)
4014 m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
4015 internalDeselect();
4016 m_textDirty = true;
4017 }
4018}
4019
4020/*!
4021 \internal
4022
4023 Adds the current selection to the undo history.
4024
4025 Returns true if there is a current selection and false otherwise.
4026*/
4027
4028bool QQuickTextInputPrivate::separateSelection()
4029{
4030 if (hasSelectedText()) {
4031 separate();
4032 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
4033 return true;
4034 } else {
4035 return false;
4036 }
4037}
4038
4039/*!
4040 \internal
4041
4042 Parses the input mask specified by \a maskFields to generate
4043 the mask data used to handle input masks.
4044*/
4045void QQuickTextInputPrivate::parseInputMask(const QString &maskFields)
4046{
4047 int delimiter = maskFields.indexOf(QLatin1Char(';'));
4048 if (maskFields.isEmpty() || delimiter == 0) {
4049 if (m_maskData) {
4050 m_maskData.reset(nullptr);
4051 m_maxLength = 32767;
4052 internalSetText(QString());
4053 }
4054 return;
4055 }
4056
4057 if (delimiter == -1) {
4058 m_blank = QLatin1Char(' ');
4059 m_inputMask = maskFields;
4060 } else {
4061 m_inputMask = maskFields.left(delimiter);
4062 m_blank = (delimiter + 1 < maskFields.size()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
4063 }
4064
4065 // calculate m_maxLength / m_maskData length
4066 m_maxLength = 0;
4067 QChar c = u'\0';
4068 for (int i=0; i<m_inputMask.size(); i++) {
4069 c = m_inputMask.at(i);
4070 if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) {
4071 m_maxLength++;
4072 continue;
4073 }
4074 if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
4075 c != QLatin1Char('<') && c != QLatin1Char('>') &&
4076 c != QLatin1Char('{') && c != QLatin1Char('}') &&
4077 c != QLatin1Char('[') && c != QLatin1Char(']'))
4078 m_maxLength++;
4079 }
4080
4081 m_maskData.reset(new MaskInputData[m_maxLength]);
4082
4083 MaskInputData::Casemode m = MaskInputData::NoCaseMode;
4084 c = u'\0';
4085 bool s;
4086 bool escape = false;
4087 int index = 0;
4088 for (int i = 0; i < m_inputMask.size(); i++) {
4089 c = m_inputMask.at(i);
4090 if (escape) {
4091 s = true;
4092 m_maskData[index].maskChar = c;
4093 m_maskData[index].separator = s;
4094 m_maskData[index].caseMode = m;
4095 index++;
4096 escape = false;
4097 } else if (c == QLatin1Char('<')) {
4098 m = MaskInputData::Lower;
4099 } else if (c == QLatin1Char('>')) {
4100 m = MaskInputData::Upper;
4101 } else if (c == QLatin1Char('!')) {
4102 m = MaskInputData::NoCaseMode;
4103 } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
4104 switch (c.unicode()) {
4105 case 'A':
4106 case 'a':
4107 case 'N':
4108 case 'n':
4109 case 'X':
4110 case 'x':
4111 case '9':
4112 case '0':
4113 case 'D':
4114 case 'd':
4115 case '#':
4116 case 'H':
4117 case 'h':
4118 case 'B':
4119 case 'b':
4120 s = false;
4121 break;
4122 case '\\':
4123 escape = true;
4124 Q_FALLTHROUGH();
4125 default:
4126 s = true;
4127 break;
4128 }
4129
4130 if (!escape) {
4131 m_maskData[index].maskChar = c;
4132 m_maskData[index].separator = s;
4133 m_maskData[index].caseMode = m;
4134 index++;
4135 }
4136 }
4137 }
4138 internalSetText(m_text);
4139}
4140
4141
4142/*!
4143 \internal
4144
4145 checks if the key is valid compared to the inputMask
4146*/
4147bool QQuickTextInputPrivate::isValidInput(QChar key, QChar mask) const
4148{
4149 switch (mask.unicode()) {
4150 case 'A':
4151 if (key.isLetter())
4152 return true;
4153 break;
4154 case 'a':
4155 if (key.isLetter() || key == m_blank)
4156 return true;
4157 break;
4158 case 'N':
4159 if (key.isLetterOrNumber())
4160 return true;
4161 break;
4162 case 'n':
4163 if (key.isLetterOrNumber() || key == m_blank)
4164 return true;
4165 break;
4166 case 'X':
4167 if (key.isPrint() && key != m_blank)
4168 return true;
4169 break;
4170 case 'x':
4171 if (key.isPrint() || key == m_blank)
4172 return true;
4173 break;
4174 case '9':
4175 if (key.isNumber())
4176 return true;
4177 break;
4178 case '0':
4179 if (key.isNumber() || key == m_blank)
4180 return true;
4181 break;
4182 case 'D':
4183 if (key.isNumber() && key.digitValue() > 0)
4184 return true;
4185 break;
4186 case 'd':
4187 if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
4188 return true;
4189 break;
4190 case '#':
4191 if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
4192 return true;
4193 break;
4194 case 'B':
4195 if (key == QLatin1Char('0') || key == QLatin1Char('1'))
4196 return true;
4197 break;
4198 case 'b':
4199 if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
4200 return true;
4201 break;
4202 case 'H':
4203 if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
4204 return true;
4205 break;
4206 case 'h':
4207 if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
4208 return true;
4209 break;
4210 default:
4211 break;
4212 }
4213 return false;
4214}
4215
4216/*!
4217 \internal
4218
4219 Returns true if the given text \a str is valid for any
4220 validator or input mask set for the line control.
4221
4222 Otherwise returns false
4223*/
4224QQuickTextInputPrivate::ValidatorState QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
4225{
4226#if QT_CONFIG(validator)
4227 QString textCopy = str;
4228 int cursorCopy = m_cursor;
4229 if (m_validator) {
4230 QValidator::State state = m_validator->validate(textCopy, cursorCopy);
4231 if (state != QValidator::Acceptable)
4232 return ValidatorState(state);
4233 }
4234#endif
4235
4236 if (!m_maskData)
4237 return AcceptableInput;
4238
4239 if (str.size() != m_maxLength)
4240 return InvalidInput;
4241
4242 for (int i=0; i < m_maxLength; ++i) {
4243 if (m_maskData[i].separator) {
4244 if (str.at(i) != m_maskData[i].maskChar)
4245 return InvalidInput;
4246 } else {
4247 if (!isValidInput(str.at(i), m_maskData[i].maskChar))
4248 return InvalidInput;
4249 }
4250 }
4251 return AcceptableInput;
4252}
4253
4254/*!
4255 \internal
4256
4257 Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
4258 specifies from where characters should be gotten when a separator is met in \a str - true means
4259 that blanks will be used, false that previous input is used.
4260 Calling this when no inputMask is set is undefined.
4261*/
4262QString QQuickTextInputPrivate::maskString(uint pos, const QString &str, bool clear) const
4263{
4264 if (pos >= (uint)m_maxLength)
4265 return QString::fromLatin1("");
4266
4267 QString fill;
4268 fill = clear ? clearString(0, m_maxLength) : m_text;
4269
4270 int strIndex = 0;
4271 QString s = QString::fromLatin1("");
4272 int i = pos;
4273 while (i < m_maxLength) {
4274 if (strIndex < str.size()) {
4275 if (m_maskData[i].separator) {
4276 s += m_maskData[i].maskChar;
4277 if (str[strIndex] == m_maskData[i].maskChar)
4278 strIndex++;
4279 ++i;
4280 } else {
4281 if (isValidInput(str[strIndex], m_maskData[i].maskChar)) {
4282 switch (m_maskData[i].caseMode) {
4283 case MaskInputData::Upper:
4284 s += str[strIndex].toUpper();
4285 break;
4286 case MaskInputData::Lower:
4287 s += str[strIndex].toLower();
4288 break;
4289 default:
4290 s += str[strIndex];
4291 }
4292 ++i;
4293 } else {
4294 // search for separator first
4295 int n = findInMask(i, true, true, str[strIndex]);
4296 if (n != -1) {
4297 if (str.size() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[strIndex]))) {
4298 s += QStringView{fill}.mid(i, n-i+1);
4299 i = n + 1; // update i to find + 1
4300 }
4301 } else {
4302 // search for valid m_blank if not
4303 n = findInMask(i, true, false, str[strIndex]);
4304 if (n != -1) {
4305 s += QStringView{fill}.mid(i, n-i);
4306 switch (m_maskData[n].caseMode) {
4307 case MaskInputData::Upper:
4308 s += str[strIndex].toUpper();
4309 break;
4310 case MaskInputData::Lower:
4311 s += str[strIndex].toLower();
4312 break;
4313 default:
4314 s += str[strIndex];
4315 }
4316 i = n + 1; // updates i to find + 1
4317 }
4318 }
4319 }
4320 ++strIndex;
4321 }
4322 } else
4323 break;
4324 }
4325
4326 return s;
4327}
4328
4329
4330
4331/*!
4332 \internal
4333
4334 Returns a "cleared" string with only separators and blank chars.
4335 Calling this when no inputMask is set is undefined.
4336*/
4337QString QQuickTextInputPrivate::clearString(uint pos, uint len) const
4338{
4339 if (pos >= (uint)m_maxLength)
4340 return QString();
4341
4342 QString s;
4343 int end = qMin((uint)m_maxLength, pos + len);
4344 for (int i = pos; i < end; ++i)
4345 if (m_maskData[i].separator)
4346 s += m_maskData[i].maskChar;
4347 else
4348 s += m_blank;
4349
4350 return s;
4351}
4352
4353/*!
4354 \internal
4355
4356 Strips blank parts of the input in a QQuickTextInputPrivate when an inputMask is set,
4357 separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
4358*/
4359QString QQuickTextInputPrivate::stripString(const QString &str) const
4360{
4361 if (!m_maskData)
4362 return str;
4363
4364 QString s;
4365 int end = qMin(m_maxLength, str.size());
4366 for (int i = 0; i < end; ++i) {
4367 if (m_maskData[i].separator)
4368 s += m_maskData[i].maskChar;
4369 else if (str[i] != m_blank)
4370 s += str[i];
4371 }
4372
4373 return s;
4374}
4375
4376/*!
4377 \internal
4378 searches forward/backward in m_maskData for either a separator or a m_blank
4379*/
4380int QQuickTextInputPrivate::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
4381{
4382 if (pos >= m_maxLength || pos < 0)
4383 return -1;
4384
4385 int end = forward ? m_maxLength : -1;
4386 int step = forward ? 1 : -1;
4387 int i = pos;
4388
4389 while (i != end) {
4390 if (findSeparator) {
4391 if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
4392 return i;
4393 } else {
4394 if (!m_maskData[i].separator) {
4395 if (searchChar.isNull())
4396 return i;
4397 else if (isValidInput(searchChar, m_maskData[i].maskChar))
4398 return i;
4399 }
4400 }
4401 i += step;
4402 }
4403 return -1;
4404}
4405
4406void QQuickTextInputPrivate::internalUndo(int until)
4407{
4408 if (!isUndoAvailable())
4409 return;
4410 cancelPasswordEchoTimer();
4411 internalDeselect();
4412 while (m_undoState && m_undoState > until) {
4413 Command& cmd = m_history[--m_undoState];
4414 switch (cmd.type) {
4415 case Insert:
4416 m_text.remove(cmd.pos, 1);
4417 m_cursor = cmd.pos;
4418 break;
4419 case SetSelection:
4420 m_selstart = cmd.selStart;
4421 m_selend = cmd.selEnd;
4422 m_cursor = cmd.pos;
4423 break;
4424 case Remove:
4425 case RemoveSelection:
4426 m_text.insert(cmd.pos, cmd.uc);
4427 m_cursor = cmd.pos + 1;
4428 break;
4429 case Delete:
4430 case DeleteSelection:
4431 m_text.insert(cmd.pos, cmd.uc);
4432 m_cursor = cmd.pos;
4433 break;
4434 case Separator:
4435 continue;
4436 }
4437 if (until < 0 && m_undoState) {
4438 Command& next = m_history[m_undoState-1];
4439 if (next.type != cmd.type
4440 && next.type < RemoveSelection
4441 && (cmd.type < RemoveSelection || next.type == Separator)) {
4442 break;
4443 }
4444 }
4445 }
4446 separate();
4447 m_textDirty = true;
4448}
4449
4450void QQuickTextInputPrivate::internalRedo()
4451{
4452 if (!isRedoAvailable())
4453 return;
4454 internalDeselect();
4455 while (m_undoState < m_history.size()) {
4456 Command& cmd = m_history[m_undoState++];
4457 switch (cmd.type) {
4458 case Insert:
4459 m_text.insert(cmd.pos, cmd.uc);
4460 m_cursor = cmd.pos + 1;
4461 break;
4462 case SetSelection:
4463 m_selstart = cmd.selStart;
4464 m_selend = cmd.selEnd;
4465 m_cursor = cmd.pos;
4466 break;
4467 case Remove:
4468 case Delete:
4469 case RemoveSelection:
4470 case DeleteSelection:
4471 m_text.remove(cmd.pos, 1);
4472 m_selstart = cmd.selStart;
4473 m_selend = cmd.selEnd;
4474 m_cursor = cmd.pos;
4475 break;
4476 case Separator:
4477 m_selstart = cmd.selStart;
4478 m_selend = cmd.selEnd;
4479 m_cursor = cmd.pos;
4480 break;
4481 }
4482 if (m_undoState < m_history.size()) {
4483 Command& next = m_history[m_undoState];
4484 if (next.type != cmd.type
4485 && cmd.type < RemoveSelection
4486 && next.type != Separator
4487 && (next.type < RemoveSelection || cmd.type == Separator)) {
4488 break;
4489 }
4490 }
4491 }
4492 m_textDirty = true;
4493}
4494
4495void QQuickTextInputPrivate::emitUndoRedoChanged()
4496{
4497 Q_Q(QQuickTextInput);
4498 const bool previousUndo = canUndo;
4499 const bool previousRedo = canRedo;
4500
4501 canUndo = isUndoAvailable();
4502 canRedo = isRedoAvailable();
4503
4504 if (previousUndo != canUndo)
4505 emit q->canUndoChanged();
4506 if (previousRedo != canRedo)
4507 emit q->canRedoChanged();
4508}
4509
4510/*!
4511 \internal
4512
4513 If the current cursor position differs from the last emitted cursor
4514 position, emits cursorPositionChanged().
4515*/
4516bool QQuickTextInputPrivate::emitCursorPositionChanged()
4517{
4518 Q_Q(QQuickTextInput);
4519 if (m_cursor != m_lastCursorPos) {
4520 m_lastCursorPos = m_cursor;
4521
4522 q->updateCursorRectangle();
4523 emit q->cursorPositionChanged();
4524
4525 if (!hasSelectedText()) {
4526 if (lastSelectionStart != m_cursor) {
4527 lastSelectionStart = m_cursor;
4528 emit q->selectionStartChanged();
4529 }
4530 if (lastSelectionEnd != m_cursor) {
4531 lastSelectionEnd = m_cursor;
4532 emit q->selectionEndChanged();
4533 }
4534 }
4535
4536#if QT_CONFIG(accessibility)
4537 if (QAccessible::isActive()) {
4538 if (QObject *acc = QQuickAccessibleAttached::findAccessible(q, QAccessible::EditableText)) {
4539 QAccessibleTextCursorEvent ev(acc, m_cursor);
4540 QAccessible::updateAccessibility(&ev);
4541 }
4542 }
4543#endif
4544
4545 return true;
4546 }
4547 return false;
4548}
4549
4550
4551void QQuickTextInputPrivate::setBlinkingCursorEnabled(bool enable)
4552{
4553 if (enable == m_blinkEnabled)
4554 return;
4555
4556 m_blinkEnabled = enable;
4557 updateCursorBlinking();
4558
4559 if (enable)
4560 connect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QQuickTextInputPrivate::updateCursorBlinking);
4561 else
4562 disconnect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QQuickTextInputPrivate::updateCursorBlinking);
4563}
4564
4565void QQuickTextInputPrivate::updateCursorBlinking()
4566{
4567 Q_Q(QQuickTextInput);
4568
4569 if (m_blinkTimer) {
4570 q->killTimer(m_blinkTimer);
4571 m_blinkTimer = 0;
4572 }
4573
4574 if (m_blinkEnabled && cursorVisible && !cursorItem && !m_readOnly) {
4575 int flashTime = QGuiApplication::styleHints()->cursorFlashTime();
4576 if (flashTime >= 2)
4577 m_blinkTimer = q->startTimer(flashTime / 2);
4578 }
4579
4580 m_blinkStatus = 1;
4581 updateType = UpdatePaintNode;
4582 q->polish();
4583 q->update();
4584}
4585
4586void QQuickTextInput::timerEvent(QTimerEvent *event)
4587{
4588 Q_D(QQuickTextInput);
4589 if (event->timerId() == d->m_blinkTimer) {
4590 d->m_blinkStatus = !d->m_blinkStatus;
4591 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
4592 polish();
4593 update();
4594 } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
4595 d->m_passwordEchoTimer.stop();
4596 d->updateDisplayText();
4597 updateCursorRectangle();
4598 }
4599}
4600
4601void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
4602{
4603 Q_Q(QQuickTextInput);
4604
4605 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
4606 if (hasAcceptableInput(m_text) == AcceptableInput || fixup()) {
4607
4608 QInputMethod *inputMethod = QGuiApplication::inputMethod();
4609 inputMethod->commit();
4610
4611 if (activeFocus) {
4612 // If we lost focus after hiding the virtual keyboard, we've already emitted
4613 // editingFinished from handleFocusEvent. Otherwise we emit it now.
4614 emit q->editingFinished();
4615 }
4616
4617 emit q->accepted();
4618 }
4619 event->ignore();
4620 return;
4621 }
4622
4623 if (m_blinkEnabled)
4624 updateCursorBlinking();
4625
4626 if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit
4627 && !m_passwordEchoEditing
4628 && !m_readOnly
4629 && !event->text().isEmpty()
4630 && !(event->modifiers() & Qt::ControlModifier)) {
4631 // Clear the edit and reset to normal echo mode while editing; the
4632 // echo mode switches back when the edit loses focus
4633 // ### resets current content. dubious code; you can
4634 // navigate with keys up, down, back, and select(?), but if you press
4635 // "left" or "right" it clears?
4636 updatePasswordEchoEditing(true);
4637 clear();
4638 }
4639
4640 bool unknown = false;
4641#if QT_CONFIG(shortcut)
4642 bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
4643#endif
4644
4645 if (false) {
4646 }
4647#if QT_CONFIG(shortcut)
4648 else if (event == QKeySequence::Undo) {
4649 q->undo();
4650 }
4651 else if (event == QKeySequence::Redo) {
4652 q->redo();
4653 }
4654 else if (event == QKeySequence::SelectAll) {
4655 selectAll();
4656 }
4657#if QT_CONFIG(clipboard)
4658 else if (event == QKeySequence::Copy) {
4659 copy();
4660 }
4661 else if (event == QKeySequence::Paste) {
4662 if (!m_readOnly) {
4663 QClipboard::Mode mode = QClipboard::Clipboard;
4664 paste(mode);
4665 }
4666 }
4667 else if (event == QKeySequence::Cut) {
4668 q->cut();
4669 }
4670 else if (event == QKeySequence::DeleteEndOfLine) {
4671 if (!m_readOnly)
4672 deleteEndOfLine();
4673 }
4674#endif // clipboard
4675 else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
4676 home(0);
4677 }
4678 else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
4679 end(0);
4680 }
4681 else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
4682 home(1);
4683 }
4684 else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
4685 end(1);
4686 }
4687 else if (event == QKeySequence::MoveToNextChar) {
4688 if (hasSelectedText()) {
4689 moveCursor(selectionEnd(), false);
4690 } else {
4691 cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
4692 }
4693 }
4694 else if (event == QKeySequence::SelectNextChar) {
4695 cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
4696 }
4697 else if (event == QKeySequence::MoveToPreviousChar) {
4698 if (hasSelectedText()) {
4699 moveCursor(selectionStart(), false);
4700 } else {
4701 cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
4702 }
4703 }
4704 else if (event == QKeySequence::SelectPreviousChar) {
4705 cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
4706 }
4707 else if (event == QKeySequence::MoveToNextWord) {
4708 if (m_echoMode == QQuickTextInput::Normal)
4709 layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
4710 else
4711 layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
4712 }
4713 else if (event == QKeySequence::MoveToPreviousWord) {
4714 if (m_echoMode == QQuickTextInput::Normal)
4715 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
4716 else if (!m_readOnly) {
4717 layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
4718 }
4719 }
4720 else if (event == QKeySequence::SelectNextWord) {
4721 if (m_echoMode == QQuickTextInput::Normal)
4722 layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
4723 else
4724 layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
4725 }
4726 else if (event == QKeySequence::SelectPreviousWord) {
4727 if (m_echoMode == QQuickTextInput::Normal)
4728 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
4729 else
4730 layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
4731 }
4732 else if (event == QKeySequence::Delete) {
4733 if (!m_readOnly)
4734 del();
4735 }
4736 else if (event == QKeySequence::DeleteEndOfWord) {
4737 if (!m_readOnly)
4738 deleteEndOfWord();
4739 }
4740 else if (event == QKeySequence::DeleteStartOfWord) {
4741 if (!m_readOnly)
4742 deleteStartOfWord();
4743 } else if (event == QKeySequence::DeleteCompleteLine) {
4744 if (!m_readOnly) {
4745 selectAll();
4746#if QT_CONFIG(clipboard)
4747 copy();
4748#endif
4749 del();
4750 }
4751 }
4752#endif // shortcut
4753 else {
4754 bool handled = false;
4755 if (event->modifiers() & Qt::ControlModifier) {
4756 switch (event->key()) {
4757 case Qt::Key_Backspace:
4758 if (!m_readOnly)
4759 deleteStartOfWord();
4760 break;
4761 default:
4762 if (!handled)
4763 unknown = true;
4764 }
4765 } else { // ### check for *no* modifier
4766 switch (event->key()) {
4767 case Qt::Key_Backspace:
4768 if (!m_readOnly) {
4769 backspace();
4770 }
4771 break;
4772 default:
4773 if (!handled)
4774 unknown = true;
4775 }
4776 }
4777 }
4778
4779 if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
4780 setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
4781 unknown = false;
4782 }
4783
4784 if (unknown && !m_readOnly) {
4785 if (m_inputControl->isAcceptableInput(event)) {
4786 if (overwriteMode
4787 // no need to call del() if we have a selection, insert
4788 // does it already
4789 && !hasSelectedText()
4790 && !(m_cursor == q_func()->text().size())) {
4791 del();
4792 }
4793
4794 insert(event->text());
4795 event->accept();
4796 return;
4797 }
4798 }
4799
4800 if (unknown)
4801 event->ignore();
4802 else
4803 event->accept();
4804}
4805
4806/*!
4807 \internal
4808
4809 Deletes the portion of the word before the current cursor position.
4810*/
4811
4812void QQuickTextInputPrivate::deleteStartOfWord()
4813{
4814 int priorState = m_undoState;
4815
4816 if (!separateSelection()) {
4817 Command cmd(SetSelection, m_cursor, u'\0', m_selstart, m_selend);
4818 separate();
4819 cursorWordBackward(true);
4820 addCommand(cmd);
4821 }
4822
4823 removeSelectedText();
4824 finishChange(priorState);
4825}
4826
4827/*!
4828 \internal
4829
4830 Deletes the portion of the word after the current cursor position.
4831*/
4832
4833void QQuickTextInputPrivate::deleteEndOfWord()
4834{
4835 int priorState = m_undoState;
4836 Command cmd(SetSelection, m_cursor, u'\0', m_selstart, m_selend);
4837 separate();
4838 cursorWordForward(true);
4839 // moveCursor (sometimes) calls separate() so we need to add the command after that so the
4840 // cursor position and selection are restored in the same undo operation as the remove.
4841 addCommand(cmd);
4842 removeSelectedText();
4843 finishChange(priorState);
4844}
4845
4846/*!
4847 \internal
4848
4849 Deletes all text from the cursor position to the end of the line.
4850*/
4851
4852void QQuickTextInputPrivate::deleteEndOfLine()
4853{
4854 int priorState = m_undoState;
4855 Command cmd(SetSelection, m_cursor, u'\0', m_selstart, m_selend);
4856 separate();
4857 setSelection(m_cursor, end());
4858 addCommand(cmd);
4859 removeSelectedText();
4860 finishChange(priorState);
4861}
4862
4863/*!
4864 \qmlmethod void QtQuick::TextInput::ensureVisible(int position)
4865 \since 5.4
4866
4867 Scrolls the contents of the text input so that the specified character
4868 \a position is visible inside the boundaries of the text input.
4869
4870 \sa autoScroll
4871*/
4872void QQuickTextInput::ensureVisible(int position)
4873{
4874 Q_D(QQuickTextInput);
4875 d->ensureVisible(position);
4876 updateCursorRectangle(false);
4877}
4878
4879/*!
4880 \qmlmethod void QtQuick::TextInput::clear()
4881 \since 5.7
4882
4883 Clears the contents of the text input
4884 and resets partial text input from an input method.
4885
4886 Use this method instead of setting the \l text property to an empty string.
4887
4888 \sa QInputMethod::reset()
4889*/
4890void QQuickTextInput::clear()
4891{
4892 Q_D(QQuickTextInput);
4893 d->cancelInput();
4894 d->clear();
4895}
4896
4897/*!
4898 \since 5.6
4899 \qmlproperty real QtQuick::TextInput::padding
4900 \qmlproperty real QtQuick::TextInput::topPadding
4901 \qmlproperty real QtQuick::TextInput::leftPadding
4902 \qmlproperty real QtQuick::TextInput::bottomPadding
4903 \qmlproperty real QtQuick::TextInput::rightPadding
4904
4905 These properties hold the padding around the content. This space is reserved
4906 in addition to the contentWidth and contentHeight.
4907
4908 The individual padding properties assume the value of the \c padding
4909 property unless they are set explicitly. For example, if \c padding is
4910 set to \c 4 and \c leftPadding to \c 8, \c 8 will be used as the left
4911 padding.
4912
4913 \note If an explicit width or height is given to a TextInput, care must be
4914 taken to ensure it is large enough to accommodate the relevant padding
4915 values. For example: if \c topPadding and \c bottomPadding are set to
4916 \c 10, but the height of the TextInput is only set to \c 20, the text will
4917 not have enough vertical space in which to be rendered, and will appear
4918 clipped.
4919*/
4920qreal QQuickTextInput::padding() const
4921{
4922 Q_D(const QQuickTextInput);
4923 return d->padding();
4924}
4925
4926void QQuickTextInput::setPadding(qreal padding)
4927{
4928 Q_D(QQuickTextInput);
4929 if (qFuzzyCompare(d->padding(), padding))
4930 return;
4931
4932 d->extra.value().padding = padding;
4933 d->updateLayout();
4934 updateCursorRectangle();
4935 emit paddingChanged();
4936 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
4937 emit topPaddingChanged();
4938 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
4939 emit leftPaddingChanged();
4940 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
4941 emit rightPaddingChanged();
4942 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
4943 emit bottomPaddingChanged();
4944}
4945
4946void QQuickTextInput::resetPadding()
4947{
4948 setPadding(0);
4949}
4950
4951qreal QQuickTextInput::topPadding() const
4952{
4953 Q_D(const QQuickTextInput);
4954 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
4955 return d->extra->topPadding;
4956 return d->padding();
4957}
4958
4959void QQuickTextInput::setTopPadding(qreal padding)
4960{
4961 Q_D(QQuickTextInput);
4962 d->setTopPadding(padding);
4963}
4964
4965void QQuickTextInput::resetTopPadding()
4966{
4967 Q_D(QQuickTextInput);
4968 d->setTopPadding(0, true);
4969}
4970
4971qreal QQuickTextInput::leftPadding() const
4972{
4973 Q_D(const QQuickTextInput);
4974 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
4975 return d->extra->leftPadding;
4976 return d->padding();
4977}
4978
4979void QQuickTextInput::setLeftPadding(qreal padding)
4980{
4981 Q_D(QQuickTextInput);
4982 d->setLeftPadding(padding);
4983}
4984
4985void QQuickTextInput::resetLeftPadding()
4986{
4987 Q_D(QQuickTextInput);
4988 d->setLeftPadding(0, true);
4989}
4990
4991qreal QQuickTextInput::rightPadding() const
4992{
4993 Q_D(const QQuickTextInput);
4994 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
4995 return d->extra->rightPadding;
4996 return d->padding();
4997}
4998
4999void QQuickTextInput::setRightPadding(qreal padding)
5000{
5001 Q_D(QQuickTextInput);
5002 d->setRightPadding(padding);
5003}
5004
5005void QQuickTextInput::resetRightPadding()
5006{
5007 Q_D(QQuickTextInput);
5008 d->setRightPadding(0, true);
5009}
5010
5011qreal QQuickTextInput::bottomPadding() const
5012{
5013 Q_D(const QQuickTextInput);
5014 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
5015 return d->extra->bottomPadding;
5016 return d->padding();
5017}
5018
5019void QQuickTextInput::setBottomPadding(qreal padding)
5020{
5021 Q_D(QQuickTextInput);
5022 d->setBottomPadding(padding);
5023}
5024
5025void QQuickTextInput::resetBottomPadding()
5026{
5027 Q_D(QQuickTextInput);
5028 d->setBottomPadding(0, true);
5029}
5030
5031#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
5032void QQuickTextInput::setOldSelectionDefault()
5033{
5034 Q_D(QQuickTextInput);
5035 d->selectByMouse = false;
5036 d->selectByTouchDrag = true;
5037 qCDebug(lcQuickTextInput, "pre-6.4 behavior chosen: selectByMouse defaults false; if enabled, touchscreen acts like a mouse");
5038}
5039
5040// TODO in 6.7.0: remove the note about versions prior to 6.4 in selectByMouse() documentation
5041QQuickPre64TextInput::QQuickPre64TextInput(QQuickItem *parent)
5042 : QQuickTextInput(parent)
5043{
5044 setOldSelectionDefault();
5045}
5046#endif
5047
5048QT_END_NAMESPACE
5049
5050#include "moc_qquicktextinput_p.cpp"
Combined button and popup list for selecting options.