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);
1964 else
1965 node->clearCursor();
1966 } else {
1967 node->setRenderType(QSGTextNode::RenderType(d->renderType));
1968 node->clear();
1969 node->setMatrix(QMatrix4x4());
1970 node->setTextStyle(QSGInternalTextNode::Normal);
1971 node->setColor(d->color);
1972 node->setSelectionTextColor(d->selectedTextColor);
1973 node->setSelectionColor(d->selectionColor);
1974 node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
1975
1976 if (flags().testFlag(ItemObservesViewport))
1977 node->setViewport(clipRect());
1978 else
1979 node->setViewport(QRectF{});
1980
1981 QPointF offset(leftPadding(), topPadding());
1982 offset -= QPointF(d->hscroll, d->vscroll);
1983
1984 if (!d->m_textLayout.text().isEmpty()
1985#if QT_CONFIG(im)
1986 || !d->m_textLayout.preeditAreaText().isEmpty()
1987#endif
1988 ) {
1989 node->addTextLayout(offset, &d->m_textLayout,
1990 d->selectionStart(),
1991 d->selectionEnd() - 1); // selectionEnd() returns first char after
1992 // selection
1993 }
1994
1995 if (showCursor)
1996 node->setCursor(cursorRectangle(), d->color);
1997
1998 d->textLayoutDirty = false;
1999 }
2000
2001 d->containsUnscalableGlyphs = node->containsUnscalableGlyphs();
2002
2003 invalidateFontCaches();
2004
2005 return node;
2006}
2007
2008#if QT_CONFIG(im)
2009QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
2010{
2011#ifdef Q_OS_ANDROID
2012 // QTBUG-61652
2013 if (property == Qt::ImEnterKeyType) {
2014 Q_D(const QQuickItem);
2015 // Do not change if type was set manually
2016 if (!d->extra.isAllocated()
2017 || d->extra->enterKeyAttached == nullptr
2018 || d->extra->enterKeyAttached->type() == Qt::EnterKeyDefault) {
2019
2020 QQuickItem *next = const_cast<QQuickTextInput*>(this)->nextItemInFocusChain();
2021 QQuickItem *originalNext = next;
2022 while (next && next != this && !next->activeFocusOnTab()) {
2023 next = next->nextItemInFocusChain();
2024 if (next == originalNext) {
2025 // There seems to be no suitable element in the focus chain
2026 next = nullptr;
2027 }
2028 }
2029 if (next) {
2030 const auto nextYPos = next->mapToGlobal(QPoint(0, 0)).y();
2031 const auto currentYPos = this->mapToGlobal(QPoint(0, 0)).y();
2032 if (currentYPos < nextYPos)
2033 // Set EnterKey to KeyNext type only if the next item
2034 // in the focus chain is below current QQuickTextInput
2035 return Qt::EnterKeyNext;
2036 }
2037 }
2038 }
2039#endif
2040 return inputMethodQuery(property, QVariant());
2041}
2042
2043QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property, const QVariant &argument) const
2044{
2045 Q_D(const QQuickTextInput);
2046 switch (property) {
2047 case Qt::ImEnabled:
2048 return QVariant((bool)(flags() & ItemAcceptsInputMethod));
2049 case Qt::ImHints:
2050 return QVariant((int) d->effectiveInputMethodHints());
2051 case Qt::ImCursorRectangle:
2052 return cursorRectangle();
2053 case Qt::ImAnchorRectangle:
2054 return d->anchorRectangle();
2055 case Qt::ImFont:
2056 return font();
2057 case Qt::ImCursorPosition: {
2058 const QPointF pt = argument.toPointF();
2059 if (!pt.isNull())
2060 return QVariant(d->positionAt(pt));
2061 return QVariant(d->m_cursor);
2062 }
2063 case Qt::ImSurroundingText:
2064 if (d->m_echoMode == PasswordEchoOnEdit && !d->m_passwordEchoEditing) {
2065 return QVariant(displayText());
2066 } else {
2067 return QVariant(d->realText());
2068 }
2069 case Qt::ImCurrentSelection:
2070 return QVariant(selectedText());
2071 case Qt::ImMaximumTextLength:
2072 return QVariant(maxLength());
2073 case Qt::ImAnchorPosition:
2074 if (d->selectionStart() == d->selectionEnd())
2075 return QVariant(d->m_cursor);
2076 else if (d->selectionStart() == d->m_cursor)
2077 return QVariant(d->selectionEnd());
2078 else
2079 return QVariant(d->selectionStart());
2080 case Qt::ImAbsolutePosition:
2081 return QVariant(d->m_cursor);
2082 case Qt::ImTextAfterCursor:
2083 if (argument.isValid())
2084 return QVariant(d->m_text.mid(d->m_cursor, argument.toInt()));
2085 return QVariant(d->m_text.mid(d->m_cursor));
2086 case Qt::ImTextBeforeCursor:
2087 if (argument.isValid())
2088 return QVariant(QStringView{d->m_text}.left(d->m_cursor).right(argument.toInt()).toString());
2089 return QVariant(d->m_text.left(d->m_cursor));
2090 case Qt::ImReadOnly:
2091 return QVariant(d->m_readOnly);
2092 default:
2093 return QQuickItem::inputMethodQuery(property);
2094 }
2095}
2096#endif // im
2097
2098/*!
2099 \qmlmethod void QtQuick::TextInput::deselect()
2100
2101 Removes active text selection.
2102*/
2103void QQuickTextInput::deselect()
2104{
2105 Q_D(QQuickTextInput);
2106 d->deselect();
2107}
2108
2109/*!
2110 \qmlmethod void QtQuick::TextInput::selectAll()
2111
2112 Causes all text to be selected.
2113*/
2114void QQuickTextInput::selectAll()
2115{
2116 Q_D(QQuickTextInput);
2117 d->setSelection(0, text().size());
2118}
2119
2120/*!
2121 \qmlmethod bool QtQuick::TextInput::isRightToLeft(int start, int end)
2122
2123 Returns \c true if the natural reading direction of the editor text
2124 found between positions \a start and \a end is right to left.
2125*/
2126bool QQuickTextInput::isRightToLeft(int start, int end)
2127{
2128 if (start > end) {
2129 qmlWarning(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
2130 return false;
2131 } else {
2132 return QStringView{text()}.mid(start, end - start).isRightToLeft();
2133 }
2134}
2135
2136#if QT_CONFIG(clipboard)
2137/*!
2138 \qmlmethod void QtQuick::TextInput::cut()
2139
2140 Moves the currently selected text to the system clipboard.
2141
2142 \note If the echo mode is set to a mode other than Normal then cut
2143 will not work. This is to prevent using cut as a method of bypassing
2144 password features of the line control.
2145*/
2146void QQuickTextInput::cut()
2147{
2148 Q_D(QQuickTextInput);
2149 if (!d->m_readOnly && d->m_echoMode == QQuickTextInput::Normal) {
2150 d->copy();
2151 d->del();
2152 }
2153}
2154
2155/*!
2156 \qmlmethod void QtQuick::TextInput::copy()
2157
2158 Copies the currently selected text to the system clipboard.
2159
2160 \note If the echo mode is set to a mode other than Normal then copy
2161 will not work. This is to prevent using copy as a method of bypassing
2162 password features of the line control.
2163*/
2164void QQuickTextInput::copy()
2165{
2166 Q_D(QQuickTextInput);
2167 d->copy();
2168}
2169
2170/*!
2171 \qmlmethod void QtQuick::TextInput::paste()
2172
2173 Replaces the currently selected text by the contents of the system clipboard.
2174*/
2175void QQuickTextInput::paste()
2176{
2177 Q_D(QQuickTextInput);
2178 if (!d->m_readOnly)
2179 d->paste();
2180}
2181#endif // clipboard
2182
2183/*!
2184 \qmlmethod void QtQuick::TextInput::undo()
2185
2186 Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
2187 current selection, and updates the selection start to the current cursor
2188 position.
2189*/
2190
2191void QQuickTextInput::undo()
2192{
2193 Q_D(QQuickTextInput);
2194 if (!d->m_readOnly) {
2195 d->cancelInput();
2196 d->internalUndo();
2197 d->finishChange(-1, true);
2198 }
2199}
2200
2201/*!
2202 \qmlmethod void QtQuick::TextInput::redo()
2203
2204 Redoes the last operation if redo is \l {canRedo}{available}.
2205*/
2206
2207void QQuickTextInput::redo()
2208{
2209 Q_D(QQuickTextInput);
2210 if (!d->m_readOnly) {
2211 d->cancelInput();
2212 d->internalRedo();
2213 d->finishChange();
2214 }
2215}
2216
2217/*!
2218 \qmlmethod void QtQuick::TextInput::insert(int position, string text)
2219
2220 Inserts \a text into the TextInput at \a position.
2221*/
2222
2223void QQuickTextInput::insert(int position, const QString &text)
2224{
2225 Q_D(QQuickTextInput);
2226 if (d->m_echoMode == QQuickTextInput::Password) {
2227 if (d->m_passwordMaskDelay > 0)
2228 d->m_passwordEchoTimer.start(d->m_passwordMaskDelay, this);
2229 }
2230 if (position < 0 || position > d->m_text.size())
2231 return;
2232
2233 const int priorState = d->m_undoState;
2234
2235 QString insertText = text;
2236
2237 if (d->hasSelectedText()) {
2238 d->addCommand(QQuickTextInputPrivate::Command(
2239 QQuickTextInputPrivate::SetSelection, d->m_cursor, u'\0', d->m_selstart, d->m_selend));
2240 }
2241 if (d->m_maskData) {
2242 insertText = d->maskString(position, insertText);
2243 for (int i = 0; i < insertText.size(); ++i) {
2244 d->addCommand(QQuickTextInputPrivate::Command(
2245 QQuickTextInputPrivate::DeleteSelection, position + i, d->m_text.at(position + i), -1, -1));
2246 d->addCommand(QQuickTextInputPrivate::Command(
2247 QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
2248 }
2249 d->m_text.replace(position, insertText.size(), insertText);
2250 if (!insertText.isEmpty())
2251 d->m_textDirty = true;
2252 if (position < d->m_selend && position + insertText.size() > d->m_selstart)
2253 d->m_selDirty = true;
2254 } else {
2255 int remaining = d->m_maxLength - d->m_text.size();
2256 if (remaining != 0) {
2257 insertText = insertText.left(remaining);
2258 d->m_text.insert(position, insertText);
2259 for (int i = 0; i < insertText.size(); ++i)
2260 d->addCommand(QQuickTextInputPrivate::Command(
2261 QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
2262 if (d->m_cursor >= position)
2263 d->m_cursor += insertText.size();
2264 if (d->m_selstart >= position)
2265 d->m_selstart += insertText.size();
2266 if (d->m_selend >= position)
2267 d->m_selend += insertText.size();
2268 d->m_textDirty = true;
2269 if (position >= d->m_selstart && position <= d->m_selend)
2270 d->m_selDirty = true;
2271 }
2272 }
2273
2274 d->addCommand(QQuickTextInputPrivate::Command(
2275 QQuickTextInputPrivate::SetSelection, d->m_cursor, u'\0', d->m_selstart, d->m_selend));
2276 d->finishChange(priorState);
2277
2278 if (d->lastSelectionStart != d->lastSelectionEnd) {
2279 if (d->m_selstart != d->lastSelectionStart) {
2280 d->lastSelectionStart = d->m_selstart;
2281 emit selectionStartChanged();
2282 }
2283 if (d->m_selend != d->lastSelectionEnd) {
2284 d->lastSelectionEnd = d->m_selend;
2285 emit selectionEndChanged();
2286 }
2287 }
2288}
2289
2290/*!
2291 \qmlmethod void QtQuick::TextInput::remove(int start, int end)
2292
2293 Removes the section of text that is between the \a start and \a end positions from the TextInput.
2294*/
2295
2296void QQuickTextInput::remove(int start, int end)
2297{
2298 Q_D(QQuickTextInput);
2299
2300 start = qBound(0, start, d->m_text.size());
2301 end = qBound(0, end, d->m_text.size());
2302
2303 if (start > end)
2304 qSwap(start, end);
2305 else if (start == end)
2306 return;
2307
2308 if (start < d->m_selend && end > d->m_selstart)
2309 d->m_selDirty = true;
2310
2311 const int priorState = d->m_undoState;
2312
2313 d->addCommand(QQuickTextInputPrivate::Command(
2314 QQuickTextInputPrivate::SetSelection, d->m_cursor, u'\0', d->m_selstart, d->m_selend));
2315
2316 if (start <= d->m_cursor && d->m_cursor < end) {
2317 // cursor is within the selection. Split up the commands
2318 // to be able to restore the correct cursor position
2319 for (int i = d->m_cursor; i >= start; --i) {
2320 d->addCommand(QQuickTextInputPrivate::Command(
2321 QQuickTextInputPrivate::DeleteSelection, i, d->m_text.at(i), -1, 1));
2322 }
2323 for (int i = end - 1; i > d->m_cursor; --i) {
2324 d->addCommand(QQuickTextInputPrivate::Command(
2325 QQuickTextInputPrivate::DeleteSelection, i - d->m_cursor + start - 1, d->m_text.at(i), -1, -1));
2326 }
2327 } else {
2328 for (int i = end - 1; i >= start; --i) {
2329 d->addCommand(QQuickTextInputPrivate::Command(
2330 QQuickTextInputPrivate::RemoveSelection, i, d->m_text.at(i), -1, -1));
2331 }
2332 }
2333 if (d->m_maskData) {
2334 d->m_text.replace(start, end - start, d->clearString(start, end - start));
2335 for (int i = 0; i < end - start; ++i) {
2336 d->addCommand(QQuickTextInputPrivate::Command(
2337 QQuickTextInputPrivate::Insert, start + i, d->m_text.at(start + i), -1, -1));
2338 }
2339 } else {
2340 d->m_text.remove(start, end - start);
2341
2342 if (d->m_cursor > start)
2343 d->m_cursor -= qMin(d->m_cursor, end) - start;
2344 if (d->m_selstart > start)
2345 d->m_selstart -= qMin(d->m_selstart, end) - start;
2346 if (d->m_selend >= end)
2347 d->m_selend -= end - start;
2348 }
2349 d->addCommand(QQuickTextInputPrivate::Command(
2350 QQuickTextInputPrivate::SetSelection, d->m_cursor, u'\0', d->m_selstart, d->m_selend));
2351
2352 d->m_textDirty = true;
2353 d->finishChange(priorState);
2354
2355 if (d->lastSelectionStart != d->lastSelectionEnd) {
2356 if (d->m_selstart != d->lastSelectionStart) {
2357 d->lastSelectionStart = d->m_selstart;
2358 emit selectionStartChanged();
2359 }
2360 if (d->m_selend != d->lastSelectionEnd) {
2361 d->lastSelectionEnd = d->m_selend;
2362 emit selectionEndChanged();
2363 }
2364 }
2365}
2366
2367
2368/*!
2369 \qmlmethod void QtQuick::TextInput::selectWord()
2370
2371 Causes the word closest to the current cursor position to be selected.
2372*/
2373void QQuickTextInput::selectWord()
2374{
2375 Q_D(QQuickTextInput);
2376 d->selectWordAtPos(d->m_cursor);
2377}
2378
2379/*!
2380 \qmlproperty string QtQuick::TextInput::passwordCharacter
2381
2382 This is the character displayed when echoMode is set to Password or
2383 PasswordEchoOnEdit. By default it is the password character used by
2384 the platform theme.
2385
2386 If this property is set to a string with more than one character,
2387 the first character is used. If the string is empty, the value
2388 is ignored and the property is not set.
2389*/
2390QString QQuickTextInput::passwordCharacter() const
2391{
2392 Q_D(const QQuickTextInput);
2393 return QString(d->m_passwordCharacter);
2394}
2395
2396void QQuickTextInput::setPasswordCharacter(const QString &str)
2397{
2398 Q_D(QQuickTextInput);
2399 if (str.size() < 1)
2400 return;
2401 d->m_passwordCharacter = str.constData()[0];
2402 if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit)
2403 d->updateDisplayText();
2404 emit passwordCharacterChanged();
2405}
2406
2407/*!
2408 \qmlproperty int QtQuick::TextInput::passwordMaskDelay
2409 \since 5.4
2410
2411 Sets the delay before visible character is masked with password character, in milliseconds.
2412
2413 The reset method will be called by assigning undefined.
2414*/
2415int QQuickTextInput::passwordMaskDelay() const
2416{
2417 Q_D(const QQuickTextInput);
2418 return d->m_passwordMaskDelay;
2419}
2420
2421void QQuickTextInput::setPasswordMaskDelay(int delay)
2422{
2423 Q_D(QQuickTextInput);
2424 if (d->m_passwordMaskDelay != delay) {
2425 d->m_passwordMaskDelay = delay;
2426 emit passwordMaskDelayChanged(delay);
2427 }
2428}
2429
2430void QQuickTextInput::resetPasswordMaskDelay()
2431{
2432 setPasswordMaskDelay(qGuiApp->styleHints()->passwordMaskDelay());
2433}
2434
2435/*!
2436 \qmlproperty string QtQuick::TextInput::displayText
2437
2438 This is the text displayed in the TextInput.
2439
2440 If \l echoMode is set to TextInput::Normal, this holds the
2441 same value as the TextInput::text property. Otherwise,
2442 this property holds the text visible to the user, while
2443 the \l text property holds the actual entered text.
2444
2445 \note Unlike the TextInput::text property, this contains
2446 partial text input from an input method.
2447
2448 \readonly
2449 \sa preeditText
2450*/
2451QString QQuickTextInput::displayText() const
2452{
2453 Q_D(const QQuickTextInput);
2454 return d->m_textLayout.text().insert(d->m_textLayout.preeditAreaPosition(), d->m_textLayout.preeditAreaText());
2455}
2456
2457/*!
2458 \qmlproperty string QtQuick::TextInput::preeditText
2459 \readonly
2460 \since 5.7
2461
2462 This property contains partial text input from an input method.
2463
2464 To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
2465 flag in inputMethodHints.
2466
2467 \sa displayText, inputMethodHints
2468*/
2469QString QQuickTextInput::preeditText() const
2470{
2471 Q_D(const QQuickTextInput);
2472 return d->m_textLayout.preeditAreaText();
2473}
2474
2475/*!
2476 \qmlproperty bool QtQuick::TextInput::selectByMouse
2477
2478 Defaults to \c true.
2479
2480 If true, the user can use the mouse to select text in the usual way.
2481
2482 \note In versions prior to 6.4, the default was \c false; but if you
2483 enabled this property, you could also select text on a touchscreen by
2484 dragging your finger across it. This interfered with flicking when
2485 TextInput was used inside a Flickable. For consistency with TextField,
2486 selectByMouse now really means what it says: if \c true, you can select
2487 text by dragging \e only with a mouse. If this change does not suit your
2488 application, you can set \c selectByMouse to \c false, or import an older
2489 API version (for example \c {import QtQuick 6.3}) to revert to the previous
2490 behavior. The option to revert behavior by changing the import version will
2491 be removed in a later version of Qt.
2492*/
2493bool QQuickTextInput::selectByMouse() const
2494{
2495 Q_D(const QQuickTextInput);
2496 return d->selectByMouse;
2497}
2498
2499void QQuickTextInput::setSelectByMouse(bool on)
2500{
2501 Q_D(QQuickTextInput);
2502 if (d->selectByMouse != on) {
2503 d->selectByMouse = on;
2504 emit selectByMouseChanged(on);
2505 }
2506}
2507
2508/*!
2509 \qmlproperty enumeration QtQuick::TextInput::mouseSelectionMode
2510
2511 Specifies how text should be selected using a mouse.
2512
2513 \value TextInput.SelectCharacters (default) The selection is updated with individual characters.
2514 \value TextInput.SelectWords The selection is updated with whole words.
2515
2516 This property only applies when \l selectByMouse is true.
2517*/
2518
2519QQuickTextInput::SelectionMode QQuickTextInput::mouseSelectionMode() const
2520{
2521 Q_D(const QQuickTextInput);
2522 return d->mouseSelectionMode;
2523}
2524
2525void QQuickTextInput::setMouseSelectionMode(SelectionMode mode)
2526{
2527 Q_D(QQuickTextInput);
2528 if (d->mouseSelectionMode != mode) {
2529 d->mouseSelectionMode = mode;
2530 emit mouseSelectionModeChanged(mode);
2531 }
2532}
2533
2534/*!
2535 \qmlproperty bool QtQuick::TextInput::persistentSelection
2536
2537 Whether the TextInput should keep its selection when it loses active focus to another
2538 item in the scene. By default this is set to false;
2539*/
2540
2541bool QQuickTextInput::persistentSelection() const
2542{
2543 Q_D(const QQuickTextInput);
2544 return d->persistentSelection;
2545}
2546
2547void QQuickTextInput::setPersistentSelection(bool on)
2548{
2549 Q_D(QQuickTextInput);
2550 if (d->persistentSelection == on)
2551 return;
2552 d->persistentSelection = on;
2553 emit persistentSelectionChanged();
2554}
2555
2556/*!
2557 \qmlproperty bool QtQuick::TextInput::canPaste
2558 \readonly
2559
2560 Returns true if the TextInput is writable and the content of the clipboard is
2561 suitable for pasting into the TextInput.
2562*/
2563bool QQuickTextInput::canPaste() const
2564{
2565#if QT_CONFIG(clipboard)
2566 Q_D(const QQuickTextInput);
2567 if (!d->canPasteValid) {
2568 bool canPaste = false;
2569 if (!d->m_readOnly) {
2570 if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
2571 canPaste = mimeData->hasText() && !mimeData->text().isEmpty();
2572 }
2573 const_cast<QQuickTextInputPrivate *>(d)->canPaste = canPaste;
2574 const_cast<QQuickTextInputPrivate *>(d)->canPasteValid = true;
2575 }
2576 return d->canPaste;
2577#else
2578 return false;
2579#endif
2580}
2581
2582/*!
2583 \qmlproperty bool QtQuick::TextInput::canUndo
2584 \readonly
2585
2586 Returns true if the TextInput is writable and there are previous operations
2587 that can be undone.
2588*/
2589
2590bool QQuickTextInput::canUndo() const
2591{
2592 Q_D(const QQuickTextInput);
2593 return d->canUndo;
2594}
2595
2596/*!
2597 \qmlproperty bool QtQuick::TextInput::canRedo
2598 \readonly
2599
2600 Returns true if the TextInput is writable and there are \l {undo}{undone}
2601 operations that can be redone.
2602*/
2603
2604bool QQuickTextInput::canRedo() const
2605{
2606 Q_D(const QQuickTextInput);
2607 return d->canRedo;
2608}
2609
2610/*!
2611 \qmlproperty real QtQuick::TextInput::contentWidth
2612 \readonly
2613
2614 Returns the width of the text, including the width past the width
2615 which is covered due to insufficient wrapping if \l wrapMode is set.
2616*/
2617
2618qreal QQuickTextInput::contentWidth() const
2619{
2620 Q_D(const QQuickTextInput);
2621 return d->contentSize.width();
2622}
2623
2624/*!
2625 \qmlproperty real QtQuick::TextInput::contentHeight
2626 \readonly
2627
2628 Returns the height of the text, including the height past the height
2629 that is covered if the text does not fit within the set height.
2630*/
2631
2632qreal QQuickTextInput::contentHeight() const
2633{
2634 Q_D(const QQuickTextInput);
2635 return d->contentSize.height();
2636}
2637
2638void QQuickTextInput::moveCursorSelection(int position)
2639{
2640 Q_D(QQuickTextInput);
2641 d->moveCursor(position, true);
2642}
2643
2644/*!
2645 \qmlmethod void QtQuick::TextInput::moveCursorSelection(int position, SelectionMode mode)
2646
2647 Moves the cursor to \a position and updates the selection according to the optional \a mode
2648 parameter. (To only move the cursor, set the \l cursorPosition property.)
2649
2650 When this method is called it additionally sets either the
2651 selectionStart or the selectionEnd (whichever was at the previous cursor position)
2652 to the specified position. This allows you to easily extend and contract the selected
2653 text range.
2654
2655 The selection mode specifies whether the selection is updated on a per character or a per word
2656 basis. If not specified the selection mode will default to \c {TextInput.SelectCharacters}.
2657
2658 \value TextInput.SelectCharacters
2659 Sets either the selectionStart or selectionEnd (whichever was at the previous cursor position)
2660 to the specified position.
2661 \value TextInput.SelectWords
2662 Sets the selectionStart and selectionEnd to include all words between the specified position
2663 and the previous cursor position. Words partially in the range are included.
2664
2665 For example, take this sequence of calls:
2666
2667 \code
2668 cursorPosition = 5
2669 moveCursorSelection(9, TextInput.SelectCharacters)
2670 moveCursorSelection(7, TextInput.SelectCharacters)
2671 \endcode
2672
2673 This moves the cursor to position 5, extend the selection end from 5 to 9
2674 and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
2675 selected (the 6th and 7th characters).
2676
2677 The same sequence with TextInput.SelectWords will extend the selection start to a word boundary
2678 before or on position 5 and extend the selection end to a word boundary on or past position 9.
2679*/
2680void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
2681{
2682 Q_D(QQuickTextInput);
2683
2684 if (mode == SelectCharacters) {
2685 d->moveCursor(pos, true);
2686 } else if (pos != d->m_cursor) {
2687 const int cursor = d->m_cursor;
2688 int anchor;
2689 if (!d->hasSelectedText())
2690 anchor = d->m_cursor;
2691 else if (d->selectionStart() == d->m_cursor)
2692 anchor = d->selectionEnd();
2693 else
2694 anchor = d->selectionStart();
2695
2696 if (anchor < pos || (anchor == pos && cursor < pos)) {
2697 const QString text = this->text();
2698 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
2699 finder.setPosition(anchor);
2700
2701 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
2702 if (anchor < text.size() && (reasons == QTextBoundaryFinder::NotAtBoundary
2703 || (reasons & QTextBoundaryFinder::EndOfItem))) {
2704 finder.toPreviousBoundary();
2705 }
2706 anchor = finder.position() != -1 ? finder.position() : 0;
2707
2708 finder.setPosition(pos);
2709 if (pos > 0 && !finder.boundaryReasons())
2710 finder.toNextBoundary();
2711 const int cursor = finder.position() != -1 ? finder.position() : text.size();
2712
2713 d->setSelection(anchor, cursor - anchor);
2714 } else if (anchor > pos || (anchor == pos && cursor > pos)) {
2715 const QString text = this->text();
2716 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
2717 finder.setPosition(anchor);
2718
2719 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
2720 if (anchor > 0 && (reasons == QTextBoundaryFinder::NotAtBoundary
2721 || (reasons & QTextBoundaryFinder::StartOfItem))) {
2722 finder.toNextBoundary();
2723 }
2724 anchor = finder.position() != -1 ? finder.position() : text.size();
2725
2726 finder.setPosition(pos);
2727 if (pos < text.size() && !finder.boundaryReasons())
2728 finder.toPreviousBoundary();
2729 const int cursor = finder.position() != -1 ? finder.position() : 0;
2730
2731 d->setSelection(anchor, cursor - anchor);
2732 }
2733 }
2734}
2735
2736void QQuickTextInput::focusInEvent(QFocusEvent *event)
2737{
2738 Q_D(QQuickTextInput);
2739 d->handleFocusEvent(event);
2740 QQuickImplicitSizeItem::focusInEvent(event);
2741}
2742
2743void QQuickTextInputPrivate::handleFocusEvent(QFocusEvent *event)
2744{
2745 Q_Q(QQuickTextInput);
2746 bool focus = event->gotFocus();
2747 if (!m_readOnly) {
2748 q->setCursorVisible(focus);
2749 setBlinkingCursorEnabled(focus);
2750 }
2751 if (focus) {
2752 q->q_updateAlignment();
2753#if QT_CONFIG(im)
2754 if (focusOnPress && !m_readOnly)
2755 qGuiApp->inputMethod()->show();
2756 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
2757 q, SLOT(q_updateAlignment()));
2758#endif
2759 } else {
2760 if ((m_passwordEchoEditing || m_passwordEchoTimer.isActive())) {
2761 updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
2762 }
2763
2764 if (event->reason() != Qt::ActiveWindowFocusReason
2765 && event->reason() != Qt::PopupFocusReason
2766 && hasSelectedText()
2767 && !persistentSelection)
2768 deselect();
2769
2770 if (hasAcceptableInput(m_text) == AcceptableInput || fixup())
2771 emit q->editingFinished();
2772
2773#if QT_CONFIG(im)
2774 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
2775 q, SLOT(q_updateAlignment()));
2776#endif
2777 }
2778}
2779
2780void QQuickTextInput::focusOutEvent(QFocusEvent *event)
2781{
2782 Q_D(QQuickTextInput);
2783 d->handleFocusEvent(event);
2784 QQuickImplicitSizeItem::focusOutEvent(event);
2785}
2786
2787void QQuickTextInputPrivate::readOnlyChanged(bool isReadOnly)
2788{
2789 Q_UNUSED(isReadOnly);
2790#if QT_CONFIG(accessibility)
2791 if (QQuickAccessibleAttached *accessibleAttached =
2792 QQuickAccessibleAttached::attachedProperties(q_func()))
2793 accessibleAttached->set_readOnly(isReadOnly);
2794#endif
2795}
2796
2797void QQuickTextInputPrivate::echoModeChanged(QQuickTextInput::EchoMode echoMode)
2798{
2799#if QT_CONFIG(accessibility)
2800 if (!QAccessible::isActive())
2801 return;
2802
2803 if (QQuickAccessibleAttached *accessibleAttached =
2804 QQuickAccessibleAttached::attachedProperties(q_func()))
2805 accessibleAttached->set_passwordEdit((echoMode == QQuickTextInput::Password
2806 || echoMode == QQuickTextInput::PasswordEchoOnEdit)
2807 ? true
2808 : false);
2809#else
2810 Q_UNUSED(echoMode);
2811#endif
2812}
2813
2814#if QT_CONFIG(accessibility)
2815void QQuickTextInputPrivate::accessibilityActiveChanged(bool active)
2816{
2817 if (!active)
2818 return;
2819
2820 Q_Q(QQuickTextInput);
2821 QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
2822 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
2823 Q_ASSERT(accessibleAttached);
2824 accessibleAttached->setRole(effectiveAccessibleRole());
2825 accessibleAttached->set_readOnly(m_readOnly);
2826 accessibleAttached->set_passwordEdit((m_echoMode == QQuickTextInput::Password
2827 || m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
2828 ? true
2829 : false);
2830}
2831
2832QAccessible::Role QQuickTextInputPrivate::accessibleRole() const
2833{
2834 return QAccessible::EditableText;
2835}
2836#endif
2837
2838/*!
2839 \qmlproperty bool QtQuick::TextInput::inputMethodComposing
2840 \readonly
2841
2842 This property holds whether the TextInput has partial text input from an
2843 input method.
2844
2845 While it is composing an input method may rely on mouse or key events from
2846 the TextInput to edit or commit the partial text. This property can be
2847 used to determine when to disable events handlers that may interfere with
2848 the correct operation of an input method.
2849*/
2850bool QQuickTextInput::isInputMethodComposing() const
2851{
2852#if !QT_CONFIG(im)
2853 return false;
2854#else
2855 Q_D(const QQuickTextInput);
2856 return d->hasImState;
2857#endif
2858}
2859
2860QQuickTextInputPrivate::ExtraData::ExtraData()
2861 : padding(0)
2862 , topPadding(0)
2863 , leftPadding(0)
2864 , rightPadding(0)
2865 , bottomPadding(0)
2866 , explicitTopPadding(false)
2867 , explicitLeftPadding(false)
2868 , explicitRightPadding(false)
2869 , explicitBottomPadding(false)
2870 , implicitResize(true)
2871{
2872}
2873
2874void QQuickTextInputPrivate::init()
2875{
2876 Q_Q(QQuickTextInput);
2877#if QT_CONFIG(clipboard)
2878 if (QGuiApplication::clipboard()->supportsSelection())
2879 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2880 else
2881#endif
2882 q->setAcceptedMouseButtons(Qt::LeftButton);
2883
2884#if QT_CONFIG(im)
2885 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2886#endif
2887 q->setFlag(QQuickItem::ItemHasContents);
2888#if QT_CONFIG(clipboard)
2889 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()),
2890 q, QQuickTextInput, SLOT(q_canPasteChanged()));
2891#endif // clipboard
2892
2893 lastSelectionStart = 0;
2894 lastSelectionEnd = 0;
2895 determineHorizontalAlignment();
2896
2897 if (!qmlDisableDistanceField()) {
2898 QTextOption option = m_textLayout.textOption();
2899 option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
2900 m_textLayout.setTextOption(option);
2901 }
2902
2903 m_inputControl = new QInputControl(QInputControl::LineEdit, q);
2904 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Fixed);
2905
2906 QObjectPrivate::connect(q, &QQuickTextInput::readOnlyChanged, this,
2907 &QQuickTextInputPrivate::readOnlyChanged);
2908 QObjectPrivate::connect(q, &QQuickTextInput::echoModeChanged, this,
2909 &QQuickTextInputPrivate::echoModeChanged);
2910}
2911
2912void QQuickTextInputPrivate::cancelInput()
2913{
2914#if QT_CONFIG(im)
2915 Q_Q(QQuickTextInput);
2916 if (!m_readOnly && q->hasActiveFocus() && qGuiApp)
2917 cancelPreedit();
2918#endif // im
2919}
2920
2921void QQuickTextInput::updateCursorRectangle(bool scroll)
2922{
2923 Q_D(QQuickTextInput);
2924 if (!isComponentComplete())
2925 return;
2926
2927 if (scroll) {
2928 d->updateHorizontalScroll();
2929 d->updateVerticalScroll();
2930 }
2931 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
2932 polish();
2933 update();
2934 emit cursorRectangleChanged();
2935 if (d->cursorItem) {
2936 QRectF r = cursorRectangle();
2937 d->cursorItem->setPosition(r.topLeft());
2938 d->cursorItem->setHeight(r.height());
2939 }
2940#if QT_CONFIG(im)
2941 updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
2942#endif
2943}
2944
2945void QQuickTextInput::selectionChanged()
2946{
2947 Q_D(QQuickTextInput);
2948 d->textLayoutDirty = true; //TODO: Only update rect in selection
2949 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
2950 polish();
2951 update();
2952 emit selectedTextChanged();
2953
2954 if (d->lastSelectionStart != d->selectionStart()) {
2955 d->lastSelectionStart = d->selectionStart();
2956 if (d->lastSelectionStart == -1)
2957 d->lastSelectionStart = d->m_cursor;
2958 emit selectionStartChanged();
2959 }
2960 if (d->lastSelectionEnd != d->selectionEnd()) {
2961 d->lastSelectionEnd = d->selectionEnd();
2962 if (d->lastSelectionEnd == -1)
2963 d->lastSelectionEnd = d->m_cursor;
2964 emit selectionEndChanged();
2965 }
2966}
2967
2968QRectF QQuickTextInput::boundingRect() const
2969{
2970 Q_D(const QQuickTextInput);
2971
2972 int cursorWidth = d->cursorItem ? 0 : 1;
2973
2974 qreal hscroll = d->hscroll;
2975 if (!d->autoScroll || d->contentSize.width() < width())
2976 hscroll -= QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign());
2977
2978 // Could include font max left/right bearings to either side of rectangle.
2979 QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height());
2980 r.setRight(r.right() + cursorWidth);
2981 return r;
2982}
2983
2984QRectF QQuickTextInput::clipRect() const
2985{
2986 Q_D(const QQuickTextInput);
2987
2988 int cursorWidth = d->cursorItem ? d->cursorItem->width() : 1;
2989
2990 // Could include font max left/right bearings to either side of rectangle.
2991 QRectF r = QQuickImplicitSizeItem::clipRect();
2992 r.setRight(r.right() + cursorWidth);
2993 return r;
2994}
2995
2996void QQuickTextInput::q_canPasteChanged()
2997{
2998 Q_D(QQuickTextInput);
2999 bool old = d->canPaste;
3000#if QT_CONFIG(clipboard)
3001 bool canPaste = false;
3002 if (!d->m_readOnly) {
3003 if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
3004 canPaste = mimeData->hasText() && !mimeData->text().isEmpty();
3005 }
3006 d->canPaste = canPaste;
3007#endif
3008
3009 bool changed = d->canPaste != old || !d->canPasteValid;
3010 d->canPasteValid = true;
3011 if (changed)
3012 emit canPasteChanged();
3013
3014}
3015
3016void QQuickTextInput::q_updateAlignment()
3017{
3018 Q_D(QQuickTextInput);
3019 if (d->determineHorizontalAlignment()) {
3020 d->updateLayout();
3021 updateCursorRectangle();
3022 }
3023}
3024
3025/*!
3026 \internal
3027
3028 Updates the display text based of the current edit text
3029 If the text has changed will emit displayTextChanged()
3030*/
3031void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
3032{
3033 QString orig = m_textLayout.text();
3034 QString str;
3035 if (m_echoMode == QQuickTextInput::NoEcho)
3036 str = QString::fromLatin1("");
3037 else
3038 str = m_text;
3039
3040 if (m_echoMode == QQuickTextInput::Password) {
3041 str.fill(m_passwordCharacter);
3042 if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.size()) {
3043 int cursor = m_cursor - 1;
3044 QChar uc = m_text.at(cursor);
3045 str[cursor] = uc;
3046 if (cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
3047 // second half of a surrogate, check if we have the first half as well,
3048 // if yes restore both at once
3049 uc = m_text.at(cursor - 1);
3050 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00)
3051 str[cursor - 1] = uc;
3052 }
3053 }
3054 } else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
3055 str.fill(m_passwordCharacter);
3056 }
3057
3058 // replace certain non-printable characters with spaces (to avoid
3059 // drawing boxes when using fonts that don't have glyphs for such
3060 // characters)
3061 QChar* uc = str.data();
3062 for (int i = 0; i < str.size(); ++i) {
3063 if (uc[i] == QChar::LineSeparator
3064 || uc[i] == QChar::ParagraphSeparator
3065 || uc[i] == QChar::ObjectReplacementCharacter)
3066 uc[i] = QChar(0x0020);
3067 }
3068
3069 if (str != orig || forceUpdate) {
3070 m_textLayout.setText(str);
3071 updateLayout(); // polish?
3072 emit q_func()->displayTextChanged();
3073 }
3074}
3075
3076qreal QQuickTextInputPrivate::calculateImplicitWidthForText(const QString &text) const
3077{
3078 Q_Q(const QQuickTextInput);
3079 QTextLayout layout(text);
3080
3081 QTextOption option = m_textLayout.textOption();
3082 option.setTextDirection(m_layoutDirection);
3083 option.setFlags(QTextOption::IncludeTrailingSpaces);
3084 option.setWrapMode(QTextOption::WrapMode(wrapMode));
3085 option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
3086 layout.setTextOption(option);
3087 layout.setFont(font);
3088#if QT_CONFIG(im)
3089 layout.setPreeditArea(m_textLayout.preeditAreaPosition(), m_textLayout.preeditAreaText());
3090#endif
3091 layout.beginLayout();
3092
3093 QTextLine line = layout.createLine();
3094 line.setLineWidth(qreal(INT_MAX));
3095 const qreal theImplicitWidth = qCeil(line.naturalTextWidth()) + q->leftPadding() + q->rightPadding();
3096
3097 layout.endLayout();
3098 return theImplicitWidth;
3099}
3100
3101qreal QQuickTextInputPrivate::getImplicitWidth() const
3102{
3103 Q_Q(const QQuickTextInput);
3104 if (!requireImplicitWidth) {
3105 QQuickTextInputPrivate *d = const_cast<QQuickTextInputPrivate *>(this);
3106 d->requireImplicitWidth = true;
3107
3108 if (q->isComponentComplete())
3109 d->implicitWidth = calculateImplicitWidthForText(m_text);
3110 }
3111 return implicitWidth;
3112}
3113
3114void QQuickTextInputPrivate::setTopPadding(qreal value, bool reset)
3115{
3116 Q_Q(QQuickTextInput);
3117 qreal oldPadding = q->topPadding();
3118 if (!reset || extra.isAllocated()) {
3119 extra.value().topPadding = value;
3120 extra.value().explicitTopPadding = !reset;
3121 }
3122 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
3123 updateLayout();
3124 q->updateCursorRectangle();
3125 emit q->topPaddingChanged();
3126 }
3127}
3128
3129void QQuickTextInputPrivate::setLeftPadding(qreal value, bool reset)
3130{
3131 Q_Q(QQuickTextInput);
3132 qreal oldPadding = q->leftPadding();
3133 if (!reset || extra.isAllocated()) {
3134 extra.value().leftPadding = value;
3135 extra.value().explicitLeftPadding = !reset;
3136 }
3137 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
3138 updateLayout();
3139 q->updateCursorRectangle();
3140 emit q->leftPaddingChanged();
3141 }
3142}
3143
3144void QQuickTextInputPrivate::setRightPadding(qreal value, bool reset)
3145{
3146 Q_Q(QQuickTextInput);
3147 qreal oldPadding = q->rightPadding();
3148 if (!reset || extra.isAllocated()) {
3149 extra.value().rightPadding = value;
3150 extra.value().explicitRightPadding = !reset;
3151 }
3152 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
3153 updateLayout();
3154 q->updateCursorRectangle();
3155 emit q->rightPaddingChanged();
3156 }
3157}
3158
3159void QQuickTextInputPrivate::setBottomPadding(qreal value, bool reset)
3160{
3161 Q_Q(QQuickTextInput);
3162 qreal oldPadding = q->bottomPadding();
3163 if (!reset || extra.isAllocated()) {
3164 extra.value().bottomPadding = value;
3165 extra.value().explicitBottomPadding = !reset;
3166 }
3167 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
3168 updateLayout();
3169 q->updateCursorRectangle();
3170 emit q->bottomPaddingChanged();
3171 }
3172}
3173
3174bool QQuickTextInputPrivate::isImplicitResizeEnabled() const
3175{
3176 return !extra.isAllocated() || extra->implicitResize;
3177}
3178
3179void QQuickTextInputPrivate::setImplicitResizeEnabled(bool enabled)
3180{
3181 if (!enabled)
3182 extra.value().implicitResize = false;
3183 else if (extra.isAllocated())
3184 extra->implicitResize = true;
3185}
3186
3187void QQuickTextInputPrivate::updateLayout()
3188{
3189 Q_Q(QQuickTextInput);
3190
3191 if (!q->isComponentComplete())
3192 return;
3193
3194
3195 QTextOption option = m_textLayout.textOption();
3196 option.setTextDirection(layoutDirection());
3197 option.setWrapMode(QTextOption::WrapMode(wrapMode));
3198 option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
3199 if (!qmlDisableDistanceField())
3200 option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
3201
3202 m_textLayout.setTextOption(option);
3203 m_textLayout.setFont(font);
3204
3205 m_textLayout.beginLayout();
3206
3207 QTextLine line = m_textLayout.createLine();
3208 if (requireImplicitWidth) {
3209 line.setLineWidth(qreal(INT_MAX));
3210 const bool wasInLayout = inLayout;
3211 inLayout = true;
3212 if (isImplicitResizeEnabled())
3213 q->setImplicitWidth(qCeil(line.naturalTextWidth()) + q->leftPadding() + q->rightPadding());
3214 inLayout = wasInLayout;
3215 if (inLayout) // probably the result of a binding loop, but by letting it
3216 return; // get this far we'll get a warning to that effect.
3217 }
3218 qreal lineWidth = q->widthValid() || !isImplicitResizeEnabled() ? q->width() - q->leftPadding() - q->rightPadding() : qreal(INT_MAX);
3219 qreal height = 0;
3220 qreal width = 0;
3221 do {
3222 line.setLineWidth(lineWidth);
3223 line.setPosition(QPointF(0, height));
3224
3225 height += line.height();
3226 width = qMax(width, line.naturalTextWidth());
3227
3228 line = m_textLayout.createLine();
3229 } while (line.isValid());
3230 m_textLayout.endLayout();
3231
3232 option.setWrapMode(QTextOption::NoWrap);
3233 m_textLayout.setTextOption(option);
3234
3235 textLayoutDirty = true;
3236
3237 const QSizeF previousSize = contentSize;
3238 contentSize = QSizeF(width, height);
3239
3240 updateType = UpdatePaintNode;
3241 q->polish();
3242 q->update();
3243
3244 if (isImplicitResizeEnabled()) {
3245 if (!requireImplicitWidth && !q->widthValid())
3246 q->setImplicitSize(width + q->leftPadding() + q->rightPadding(), height + q->topPadding() + q->bottomPadding());
3247 else
3248 q->setImplicitHeight(height + q->topPadding() + q->bottomPadding());
3249 }
3250
3251 updateBaselineOffset();
3252
3253 if (previousSize != contentSize)
3254 emit q->contentSizeChanged();
3255}
3256
3257/*!
3258 \internal
3259 \brief QQuickTextInputPrivate::updateBaselineOffset
3260
3261 Assumes contentSize.height() is already calculated.
3262 */
3263void QQuickTextInputPrivate::updateBaselineOffset()
3264{
3265 Q_Q(QQuickTextInput);
3266 if (!q->isComponentComplete())
3267 return;
3268 QFontMetricsF fm(font);
3269 qreal yoff = 0;
3270 if (q->heightValid()) {
3271 const qreal surplusHeight = q->height() - contentSize.height() - q->topPadding() - q->bottomPadding();
3272 if (vAlign == QQuickTextInput::AlignBottom)
3273 yoff = surplusHeight;
3274 else if (vAlign == QQuickTextInput::AlignVCenter)
3275 yoff = surplusHeight/2;
3276 }
3277 qreal ascent;
3278 if (m_textLayout.lineCount() > 0) {
3279 QTextLine line = m_textLayout.lineAt(0);
3280 ascent = line.y() + line.ascent();
3281 } else {
3282 ascent = fm.ascent();
3283 }
3284 q->setBaselineOffset(ascent + yoff + q->topPadding());
3285}
3286
3287#if QT_CONFIG(clipboard)
3288/*!
3289 \internal
3290
3291 Copies the currently selected text into the clipboard using the given
3292 \a mode.
3293
3294 \note If the echo mode is set to a mode other than Normal then copy
3295 will not work. This is to prevent using copy as a method of bypassing
3296 password features of the line control.
3297*/
3298void QQuickTextInputPrivate::copy(QClipboard::Mode mode) const
3299{
3300 QString t = selectedText();
3301 if (!t.isEmpty() && m_echoMode == QQuickTextInput::Normal) {
3302 QGuiApplication::clipboard()->setText(t, mode);
3303 }
3304}
3305
3306/*!
3307 \internal
3308
3309 Inserts the text stored in the application clipboard into the line
3310 control.
3311
3312 \sa insert()
3313*/
3314void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode)
3315{
3316 QString clip = QGuiApplication::clipboard()->text(clipboardMode);
3317 if (!clip.isEmpty() || hasSelectedText()) {
3318 separate(); //make it a separate undo/redo command
3319 insert(clip);
3320 separate();
3321 }
3322}
3323
3324#endif // clipboard
3325
3326#if QT_CONFIG(im)
3327/*!
3328 \internal
3329*/
3330void QQuickTextInputPrivate::commitPreedit()
3331{
3332 Q_Q(QQuickTextInput);
3333
3334 if (!hasImState)
3335 return;
3336
3337 QGuiApplication::inputMethod()->commit();
3338
3339 if (!hasImState)
3340 return;
3341
3342 QInputMethodEvent ev;
3343 QCoreApplication::sendEvent(q, &ev);
3344}
3345
3346void QQuickTextInputPrivate::cancelPreedit()
3347{
3348 Q_Q(QQuickTextInput);
3349
3350 if (!hasImState)
3351 return;
3352
3353 QGuiApplication::inputMethod()->reset();
3354
3355 QInputMethodEvent ev;
3356 QCoreApplication::sendEvent(q, &ev);
3357}
3358#endif // im
3359
3360/*!
3361 \internal
3362
3363 Handles the behavior for the backspace key or function.
3364 Removes the current selection if there is a selection, otherwise
3365 removes the character prior to the cursor position.
3366
3367 \sa del()
3368*/
3369void QQuickTextInputPrivate::backspace()
3370{
3371 int priorState = m_undoState;
3372 if (separateSelection()) {
3373 removeSelectedText();
3374 } else if (m_cursor) {
3375 --m_cursor;
3376 if (m_maskData)
3377 m_cursor = prevMaskBlank(m_cursor);
3378 QChar uc = m_text.at(m_cursor);
3379 if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
3380 // second half of a surrogate, check if we have the first half as well,
3381 // if yes delete both at once
3382 uc = m_text.at(m_cursor - 1);
3383 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
3384 internalDelete(true);
3385 --m_cursor;
3386 }
3387 }
3388 internalDelete(true);
3389 }
3390 finishChange(priorState);
3391}
3392
3393/*!
3394 \internal
3395
3396 Handles the behavior for the delete key or function.
3397 Removes the current selection if there is a selection, otherwise
3398 removes the character after the cursor position.
3399
3400 \sa del()
3401*/
3402void QQuickTextInputPrivate::del()
3403{
3404 int priorState = m_undoState;
3405 if (separateSelection()) {
3406 removeSelectedText();
3407 } else {
3408 int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
3409 while (n--)
3410 internalDelete();
3411 }
3412 finishChange(priorState);
3413}
3414
3415/*!
3416 \internal
3417
3418 Inserts the given \a newText at the current cursor position.
3419 If there is any selected text it is removed prior to insertion of
3420 the new text.
3421*/
3422void QQuickTextInputPrivate::insert(const QString &newText)
3423{
3424 int priorState = m_undoState;
3425 if (separateSelection())
3426 removeSelectedText();
3427 internalInsert(newText);
3428 finishChange(priorState);
3429}
3430
3431/*!
3432 \internal
3433
3434 Clears the line control text.
3435*/
3436void QQuickTextInputPrivate::clear()
3437{
3438 int priorState = m_undoState;
3439 separateSelection();
3440 m_selstart = 0;
3441 m_selend = m_text.size();
3442 removeSelectedText();
3443 separate();
3444 finishChange(priorState, /*update*/false, /*edited*/false);
3445}
3446
3447/*!
3448 \internal
3449
3450 Sets \a length characters from the given \a start position as selected.
3451 The given \a start position must be within the current text for
3452 the line control. If \a length characters cannot be selected, then
3453 the selection will extend to the end of the current text.
3454*/
3455void QQuickTextInputPrivate::setSelection(int start, int length)
3456{
3457 Q_Q(QQuickTextInput);
3458#if QT_CONFIG(im)
3459 commitPreedit();
3460#endif
3461
3462 if (start < 0 || start > m_text.size()) {
3463 qWarning("QQuickTextInputPrivate::setSelection: Invalid start position");
3464 return;
3465 }
3466
3467 if (length > 0) {
3468 if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
3469 return;
3470 m_selstart = start;
3471 m_selend = qMin(start + length, m_text.size());
3472 m_cursor = m_selend;
3473 } else if (length < 0) {
3474 if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
3475 return;
3476 m_selstart = qMax(start + length, 0);
3477 m_selend = start;
3478 m_cursor = m_selstart;
3479 } else if (m_selstart != m_selend) {
3480 m_selstart = 0;
3481 m_selend = 0;
3482 m_cursor = start;
3483 } else {
3484 m_cursor = start;
3485 emitCursorPositionChanged();
3486 return;
3487 }
3488 emit q->selectionChanged();
3489 emitCursorPositionChanged();
3490#if QT_CONFIG(im)
3491 q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImCursorPosition | Qt::ImAnchorPosition
3492 | Qt::ImCurrentSelection);
3493#endif
3494}
3495
3496/*!
3497 \internal
3498
3499 Sets the password echo editing to \a editing. If password echo editing
3500 is true, then the text of the password is displayed even if the echo
3501 mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing
3502 does not affect other echo modes.
3503*/
3504void QQuickTextInputPrivate::updatePasswordEchoEditing(bool editing)
3505{
3506 cancelPasswordEchoTimer();
3507 m_passwordEchoEditing = editing;
3508 updateDisplayText();
3509}
3510
3511/*!
3512 \internal
3513
3514 Fixes the current text so that it is valid given any set validators.
3515
3516 Returns true if the text was changed. Otherwise returns false.
3517*/
3518bool QQuickTextInputPrivate::fixup() // this function assumes that validate currently returns != Acceptable
3519{
3520#if QT_CONFIG(validator)
3521 if (m_validator) {
3522 QString textCopy = m_text;
3523 int cursorCopy = m_cursor;
3524 m_validator->fixup(textCopy);
3525 if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
3526 if (textCopy != m_text || cursorCopy != m_cursor)
3527 internalSetText(textCopy, cursorCopy);
3528 return true;
3529 }
3530 }
3531#endif
3532 return false;
3533}
3534
3535/*!
3536 \internal
3537
3538 Moves the cursor to the given position \a pos. If \a mark is true will
3539 adjust the currently selected text.
3540*/
3541void QQuickTextInputPrivate::moveCursor(int pos, bool mark)
3542{
3543 Q_Q(QQuickTextInput);
3544#if QT_CONFIG(im)
3545 commitPreedit();
3546#endif
3547
3548 if (pos != m_cursor) {
3549 separate();
3550 if (m_maskData)
3551 pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
3552 }
3553 if (mark) {
3554 int anchor;
3555 if (m_selend > m_selstart && m_cursor == m_selstart)
3556 anchor = m_selend;
3557 else if (m_selend > m_selstart && m_cursor == m_selend)
3558 anchor = m_selstart;
3559 else
3560 anchor = m_cursor;
3561 m_selstart = qMin(anchor, pos);
3562 m_selend = qMax(anchor, pos);
3563 } else {
3564 internalDeselect();
3565 }
3566 m_cursor = pos;
3567 if (mark || m_selDirty) {
3568 m_selDirty = false;
3569 emit q->selectionChanged();
3570 }
3571 emitCursorPositionChanged();
3572#if QT_CONFIG(im)
3573 q->updateInputMethod();
3574#endif
3575}
3576
3577#if QT_CONFIG(im)
3578/*!
3579 \internal
3580
3581 Applies the given input method event \a event to the text of the line
3582 control
3583*/
3584void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
3585{
3586 Q_Q(QQuickTextInput);
3587
3588 int priorState = -1;
3589 bool isGettingInput = !event->commitString().isEmpty()
3590 || event->preeditString() != preeditAreaText()
3591 || event->replacementLength() > 0;
3592 bool cursorPositionChanged = false;
3593 bool selectionChange = false;
3594 m_preeditDirty = event->preeditString() != preeditAreaText();
3595
3596 if (isGettingInput) {
3597 // If any text is being input, remove selected text.
3598 priorState = m_undoState;
3599 separateSelection();
3600 if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
3601 updatePasswordEchoEditing(true);
3602 m_selstart = 0;
3603 m_selend = m_text.size();
3604 }
3605 removeSelectedText();
3606 }
3607
3608 int c = m_cursor; // cursor position after insertion of commit string
3609 if (event->replacementStart() <= 0)
3610 c += event->commitString().size() - qMin(-event->replacementStart(), event->replacementLength());
3611
3612 int cursorInsertPos = m_cursor + event->replacementStart();
3613 if (cursorInsertPos < 0)
3614 cursorInsertPos = 0;
3615
3616 // insert commit string
3617 if (event->replacementLength()) {
3618 m_selstart = cursorInsertPos;
3619 m_selend = m_selstart + event->replacementLength();
3620 m_selend = qMin(m_selend, m_text.size());
3621 removeSelectedText();
3622 }
3623 m_cursor = cursorInsertPos;
3624
3625 if (!event->commitString().isEmpty()) {
3626 internalInsert(event->commitString());
3627 cursorPositionChanged = true;
3628 } else {
3629 m_cursor = qBound(0, c, m_text.size());
3630 }
3631
3632 for (int i = 0; i < event->attributes().size(); ++i) {
3633 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
3634 if (a.type == QInputMethodEvent::Selection) {
3635 // If we already called internalInsert(), the cursor position will
3636 // already be adjusted correctly. The attribute.start does
3637 // not seem to take the mask into account, so it will reset the cursor
3638 // to an invalid position in such case. However, when the input mask
3639 // is not active, we must apply the cursor position regardless of the
3640 // commit string.
3641 if (!cursorPositionChanged || !m_maskData)
3642 m_cursor = qBound(0, a.start + a.length, m_text.size());
3643 if (a.length) {
3644 m_selstart = qMax(0, qMin(a.start, m_text.size()));
3645 m_selend = m_cursor;
3646 if (m_selend < m_selstart) {
3647 qSwap(m_selstart, m_selend);
3648 }
3649 selectionChange = true;
3650 } else {
3651 selectionChange = m_selstart != m_selend;
3652 m_selstart = m_selend = 0;
3653 }
3654 cursorPositionChanged = true;
3655 }
3656 }
3657 QString oldPreeditString = m_textLayout.preeditAreaText();
3658 m_textLayout.setPreeditArea(m_cursor, event->preeditString());
3659 if (oldPreeditString != m_textLayout.preeditAreaText()) {
3660 emit q->preeditTextChanged();
3661 if (!event->preeditString().isEmpty() && m_undoPreeditState == -1)
3662 // Pre-edit text started. Remember state for undo purpose.
3663 m_undoPreeditState = priorState;
3664 }
3665 const int oldPreeditCursor = m_preeditCursor;
3666 m_preeditCursor = event->preeditString().size();
3667 hasImState = !event->preeditString().isEmpty();
3668 bool cursorVisible = true;
3669 QList<QTextLayout::FormatRange> formats;
3670 for (int i = 0; i < event->attributes().size(); ++i) {
3671 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
3672 if (a.type == QInputMethodEvent::Cursor) {
3673 hasImState = true;
3674 m_preeditCursor = a.start;
3675 cursorVisible = a.length != 0;
3676 } else if (a.type == QInputMethodEvent::TextFormat) {
3677 hasImState = true;
3678 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
3679 if (f.isValid()) {
3680 QTextLayout::FormatRange o;
3681 o.start = a.start + m_cursor;
3682 o.length = a.length;
3683 o.format = f;
3684 formats.append(o);
3685 }
3686 }
3687 }
3688 m_textLayout.setFormats(formats);
3689
3690 // Set cursor visible state. Do this before updating the text,
3691 // since user code connected to onTextChanged may set a different
3692 // cursor visible state (for instance by setting the focus), which
3693 // we don't want to overwrite.
3694 q->setCursorVisible(cursorVisible);
3695
3696 updateDisplayText(/*force*/ true);
3697 if (cursorPositionChanged && emitCursorPositionChanged())
3698 q->updateInputMethod(Qt::ImCursorPosition | Qt::ImAnchorPosition);
3699 else if (m_preeditCursor != oldPreeditCursor || isGettingInput)
3700 q->updateCursorRectangle();
3701
3702 if (isGettingInput)
3703 finishChange(priorState);
3704
3705 if (selectionChange) {
3706 emit q->selectionChanged();
3707 q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle
3708 | Qt::ImCurrentSelection);
3709 }
3710
3711 // Empty pre-edit text handled. Clean m_undoPreeditState
3712 if (event->preeditString().isEmpty())
3713 m_undoPreeditState = -1;
3714
3715}
3716#endif // im
3717
3718/*!
3719 \internal
3720
3721 Sets the selection to cover the word at the given cursor position.
3722 The word boundaries are defined by the behavior of QTextLayout::SkipWords
3723 cursor mode.
3724*/
3725void QQuickTextInputPrivate::selectWordAtPos(int cursor)
3726{
3727 int next = cursor + 1;
3728 if (next > end())
3729 --next;
3730 int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords);
3731 moveCursor(c, false);
3732 // ## text layout should support end of words.
3733 int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
3734 while (end > cursor && m_text.at(end - 1).isSpace())
3735 --end;
3736 moveCursor(end, true);
3737}
3738
3739/*!
3740 \internal
3741
3742 Completes a change to the line control text. If the change is not valid
3743 will undo the line control state back to the given \a validateFromState.
3744
3745 If \a edited is true and the change is valid, will emit textEdited() in
3746 addition to textChanged(). Otherwise only emits textChanged() on a valid
3747 change.
3748
3749 The \a update value is currently unused.
3750*/
3751bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bool edited)
3752{
3753 Q_Q(QQuickTextInput);
3754
3755 Q_UNUSED(update);
3756#if QT_CONFIG(im)
3757 bool inputMethodAttributesChanged = m_textDirty || m_selDirty;
3758#endif
3759 bool alignmentChanged = false;
3760 bool textChanged = false;
3761
3762 if (m_textDirty) {
3763 // do validation
3764 bool wasValidInput = m_validInput;
3765 bool wasAcceptable = m_acceptableInput;
3766 m_validInput = true;
3767 m_acceptableInput = true;
3768#if QT_CONFIG(validator)
3769 if (m_validator) {
3770 QString textCopy = m_text;
3771 if (m_maskData)
3772 textCopy = maskString(0, m_text, true);
3773 int cursorCopy = m_cursor;
3774 QValidator::State state = m_validator->validate(textCopy, cursorCopy);
3775 if (m_maskData)
3776 textCopy = m_text;
3777 m_validInput = state != QValidator::Invalid;
3778 m_acceptableInput = state == QValidator::Acceptable;
3779 if (m_validInput && !m_maskData) {
3780 if (m_text != textCopy) {
3781 internalSetText(textCopy, cursorCopy);
3782 return true;
3783 }
3784 m_cursor = cursorCopy;
3785 }
3786 }
3787#endif
3788 if (m_maskData)
3789 checkIsValid();
3790
3791#if QT_CONFIG(im)
3792 // If we were during pre-edit, validateFromState should point to the state before pre-edit
3793 // has been started. Choose the correct oldest remembered state
3794 if (m_undoPreeditState >= 0 && (m_undoPreeditState < validateFromState || validateFromState < 0))
3795 validateFromState = m_undoPreeditState;
3796#endif
3797 if (validateFromState >= 0 && wasValidInput && !m_validInput) {
3798 if (m_transactions.size())
3799 return false;
3800 internalUndo(validateFromState);
3801 m_history.resize(m_undoState);
3802 m_validInput = true;
3803 m_acceptableInput = wasAcceptable;
3804 m_textDirty = false;
3805 }
3806
3807 if (m_textDirty) {
3808 textChanged = true;
3809 m_textDirty = false;
3810#if QT_CONFIG(im)
3811 m_preeditDirty = false;
3812#endif
3813 alignmentChanged = determineHorizontalAlignment();
3814 if (edited)
3815 emit q->textEdited();
3816 emit q->textChanged();
3817 }
3818
3819 updateDisplayText(alignmentChanged);
3820
3821 if (m_acceptableInput != wasAcceptable)
3822 emit q->acceptableInputChanged();
3823 }
3824#if QT_CONFIG(im)
3825 if (m_preeditDirty) {
3826 m_preeditDirty = false;
3827 if (determineHorizontalAlignment()) {
3828 alignmentChanged = true;
3829 updateLayout();
3830 }
3831 }
3832#endif
3833
3834 if (m_selDirty) {
3835 m_selDirty = false;
3836 emit q->selectionChanged();
3837 }
3838
3839#if QT_CONFIG(im)
3840 inputMethodAttributesChanged |= (m_cursor != m_lastCursorPos);
3841 if (inputMethodAttributesChanged)
3842 q->updateInputMethod();
3843#endif
3844 emitUndoRedoChanged();
3845
3846 if (!emitCursorPositionChanged() && (alignmentChanged || textChanged))
3847 q->updateCursorRectangle();
3848
3849 return true;
3850}
3851
3852/*!
3853 \internal
3854
3855 An internal function for setting the text of the line control.
3856*/
3857void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool edited)
3858{
3859 internalDeselect();
3860 QString oldText = m_text;
3861 if (m_maskData) {
3862 m_text = maskString(0, txt, true);
3863 m_text += clearString(m_text.size(), m_maxLength - m_text.size());
3864 } else {
3865 m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
3866 }
3867 m_history.clear();
3868 m_undoState = 0;
3869#if QT_CONFIG(im)
3870 m_undoPreeditState = -1;
3871#endif
3872 m_cursor = (pos < 0 || pos > m_text.size()) ? m_text.size() : pos;
3873 m_textDirty = (oldText != m_text);
3874
3875 bool changed = finishChange(-1, true, edited);
3876#if !QT_CONFIG(accessibility)
3877 Q_UNUSED(changed);
3878#else
3879 Q_Q(QQuickTextInput);
3880 if (changed && QAccessible::isActive()) {
3881 if (QObject *acc = QQuickAccessibleAttached::findAccessible(q, QAccessible::EditableText)) {
3882 QAccessibleTextUpdateEvent ev(acc, 0, oldText, m_text);
3883 QAccessible::updateAccessibility(&ev);
3884 }
3885 }
3886#endif
3887}
3888
3889
3890/*!
3891 \internal
3892
3893 Adds the given \a command to the undo history
3894 of the line control. Does not apply the command.
3895*/
3896void QQuickTextInputPrivate::addCommand(const Command &cmd)
3897{
3898 if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) {
3899 m_history.resize(m_undoState + 2);
3900 m_history[m_undoState++] = Command(Separator, m_cursor, u'\0', m_selstart, m_selend);
3901 } else {
3902 m_history.resize(m_undoState + 1);
3903 }
3904 m_separator = false;
3905 m_history[m_undoState++] = cmd;
3906}
3907
3908/*!
3909 \internal
3910
3911 Inserts the given string \a s into the line
3912 control.
3913
3914 Also adds the appropriate commands into the undo history.
3915 This function does not call finishChange(), and may leave the text
3916 in an invalid state.
3917*/
3918void QQuickTextInputPrivate::internalInsert(const QString &s)
3919{
3920 Q_Q(QQuickTextInput);
3921 if (m_echoMode == QQuickTextInput::Password) {
3922 if (m_passwordMaskDelay > 0)
3923 m_passwordEchoTimer.start(m_passwordMaskDelay, q);
3924 }
3925 Q_ASSERT(!hasSelectedText()); // insert(), processInputMethodEvent() call removeSelectedText() first.
3926 if (m_maskData) {
3927 QString ms = maskString(m_cursor, s);
3928 for (int i = 0; i < ms.size(); ++i) {
3929 addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
3930 addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
3931 }
3932 m_text.replace(m_cursor, ms.size(), ms);
3933 m_cursor += ms.size();
3934 m_cursor = nextMaskBlank(m_cursor);
3935 m_textDirty = true;
3936 } else {
3937 int remaining = m_maxLength - m_text.size();
3938 if (remaining != 0) {
3939 const QStringView remainingStr = QStringView{s}.left(remaining);
3940 m_text.insert(m_cursor, remainingStr);
3941 for (auto e : remainingStr)
3942 addCommand(Command(Insert, m_cursor++, e, -1, -1));
3943 m_textDirty = true;
3944 }
3945 }
3946}
3947
3948/*!
3949 \internal
3950
3951 deletes a single character from the current text. If \a wasBackspace,
3952 the character prior to the cursor is removed. Otherwise the character
3953 after the cursor is removed.
3954
3955 Also adds the appropriate commands into the undo history.
3956 This function does not call finishChange(), and may leave the text
3957 in an invalid state.
3958*/
3959void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
3960{
3961 if (m_cursor < m_text.size()) {
3962 cancelPasswordEchoTimer();
3963 Q_ASSERT(!hasSelectedText()); // del(), backspace() call removeSelectedText() first.
3964 addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
3965 m_cursor, m_text.at(m_cursor), -1, -1));
3966 if (m_maskData) {
3967 m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
3968 addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
3969 } else {
3970 m_text.remove(m_cursor, 1);
3971 }
3972 m_textDirty = true;
3973 }
3974}
3975
3976/*!
3977 \internal
3978
3979 removes the currently selected text from the line control.
3980
3981 Also adds the appropriate commands into the undo history.
3982 This function does not call finishChange(), and may leave the text
3983 in an invalid state.
3984*/
3985void QQuickTextInputPrivate::removeSelectedText()
3986{
3987 if (m_selstart < m_selend && m_selend <= m_text.size()) {
3988 cancelPasswordEchoTimer();
3989 int i ;
3990 if (m_selstart <= m_cursor && m_cursor < m_selend) {
3991 // cursor is within the selection. Split up the commands
3992 // to be able to restore the correct cursor position
3993 for (i = m_cursor; i >= m_selstart; --i)
3994 addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
3995 for (i = m_selend - 1; i > m_cursor; --i)
3996 addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
3997 } else {
3998 for (i = m_selend-1; i >= m_selstart; --i)
3999 addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
4000 }
4001 if (m_maskData) {
4002 m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
4003 for (int i = 0; i < m_selend - m_selstart; ++i)
4004 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
4005 } else {
4006 m_text.remove(m_selstart, m_selend - m_selstart);
4007 }
4008 if (m_cursor > m_selstart)
4009 m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
4010 internalDeselect();
4011 m_textDirty = true;
4012 }
4013}
4014
4015/*!
4016 \internal
4017
4018 Adds the current selection to the undo history.
4019
4020 Returns true if there is a current selection and false otherwise.
4021*/
4022
4023bool QQuickTextInputPrivate::separateSelection()
4024{
4025 if (hasSelectedText()) {
4026 separate();
4027 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
4028 return true;
4029 } else {
4030 return false;
4031 }
4032}
4033
4034/*!
4035 \internal
4036
4037 Parses the input mask specified by \a maskFields to generate
4038 the mask data used to handle input masks.
4039*/
4040void QQuickTextInputPrivate::parseInputMask(const QString &maskFields)
4041{
4042 int delimiter = maskFields.indexOf(QLatin1Char(';'));
4043 if (maskFields.isEmpty() || delimiter == 0) {
4044 if (m_maskData) {
4045 m_maskData.reset(nullptr);
4046 m_maxLength = 32767;
4047 internalSetText(QString());
4048 }
4049 return;
4050 }
4051
4052 if (delimiter == -1) {
4053 m_blank = QLatin1Char(' ');
4054 m_inputMask = maskFields;
4055 } else {
4056 m_inputMask = maskFields.left(delimiter);
4057 m_blank = (delimiter + 1 < maskFields.size()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
4058 }
4059
4060 // calculate m_maxLength / m_maskData length
4061 m_maxLength = 0;
4062 QChar c = u'\0';
4063 for (int i=0; i<m_inputMask.size(); i++) {
4064 c = m_inputMask.at(i);
4065 if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) {
4066 m_maxLength++;
4067 continue;
4068 }
4069 if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
4070 c != QLatin1Char('<') && c != QLatin1Char('>') &&
4071 c != QLatin1Char('{') && c != QLatin1Char('}') &&
4072 c != QLatin1Char('[') && c != QLatin1Char(']'))
4073 m_maxLength++;
4074 }
4075
4076 m_maskData.reset(new MaskInputData[m_maxLength]);
4077
4078 MaskInputData::Casemode m = MaskInputData::NoCaseMode;
4079 c = u'\0';
4080 bool s;
4081 bool escape = false;
4082 int index = 0;
4083 for (int i = 0; i < m_inputMask.size(); i++) {
4084 c = m_inputMask.at(i);
4085 if (escape) {
4086 s = true;
4087 m_maskData[index].maskChar = c;
4088 m_maskData[index].separator = s;
4089 m_maskData[index].caseMode = m;
4090 index++;
4091 escape = false;
4092 } else if (c == QLatin1Char('<')) {
4093 m = MaskInputData::Lower;
4094 } else if (c == QLatin1Char('>')) {
4095 m = MaskInputData::Upper;
4096 } else if (c == QLatin1Char('!')) {
4097 m = MaskInputData::NoCaseMode;
4098 } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
4099 switch (c.unicode()) {
4100 case 'A':
4101 case 'a':
4102 case 'N':
4103 case 'n':
4104 case 'X':
4105 case 'x':
4106 case '9':
4107 case '0':
4108 case 'D':
4109 case 'd':
4110 case '#':
4111 case 'H':
4112 case 'h':
4113 case 'B':
4114 case 'b':
4115 s = false;
4116 break;
4117 case '\\':
4118 escape = true;
4119 Q_FALLTHROUGH();
4120 default:
4121 s = true;
4122 break;
4123 }
4124
4125 if (!escape) {
4126 m_maskData[index].maskChar = c;
4127 m_maskData[index].separator = s;
4128 m_maskData[index].caseMode = m;
4129 index++;
4130 }
4131 }
4132 }
4133 internalSetText(m_text);
4134}
4135
4136
4137/*!
4138 \internal
4139
4140 checks if the key is valid compared to the inputMask
4141*/
4142bool QQuickTextInputPrivate::isValidInput(QChar key, QChar mask) const
4143{
4144 switch (mask.unicode()) {
4145 case 'A':
4146 if (key.isLetter())
4147 return true;
4148 break;
4149 case 'a':
4150 if (key.isLetter() || key == m_blank)
4151 return true;
4152 break;
4153 case 'N':
4154 if (key.isLetterOrNumber())
4155 return true;
4156 break;
4157 case 'n':
4158 if (key.isLetterOrNumber() || key == m_blank)
4159 return true;
4160 break;
4161 case 'X':
4162 if (key.isPrint() && key != m_blank)
4163 return true;
4164 break;
4165 case 'x':
4166 if (key.isPrint() || key == m_blank)
4167 return true;
4168 break;
4169 case '9':
4170 if (key.isNumber())
4171 return true;
4172 break;
4173 case '0':
4174 if (key.isNumber() || key == m_blank)
4175 return true;
4176 break;
4177 case 'D':
4178 if (key.isNumber() && key.digitValue() > 0)
4179 return true;
4180 break;
4181 case 'd':
4182 if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
4183 return true;
4184 break;
4185 case '#':
4186 if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
4187 return true;
4188 break;
4189 case 'B':
4190 if (key == QLatin1Char('0') || key == QLatin1Char('1'))
4191 return true;
4192 break;
4193 case 'b':
4194 if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
4195 return true;
4196 break;
4197 case 'H':
4198 if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
4199 return true;
4200 break;
4201 case 'h':
4202 if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
4203 return true;
4204 break;
4205 default:
4206 break;
4207 }
4208 return false;
4209}
4210
4211/*!
4212 \internal
4213
4214 Returns true if the given text \a str is valid for any
4215 validator or input mask set for the line control.
4216
4217 Otherwise returns false
4218*/
4219QQuickTextInputPrivate::ValidatorState QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
4220{
4221#if QT_CONFIG(validator)
4222 QString textCopy = str;
4223 int cursorCopy = m_cursor;
4224 if (m_validator) {
4225 QValidator::State state = m_validator->validate(textCopy, cursorCopy);
4226 if (state != QValidator::Acceptable)
4227 return ValidatorState(state);
4228 }
4229#endif
4230
4231 if (!m_maskData)
4232 return AcceptableInput;
4233
4234 if (str.size() != m_maxLength)
4235 return InvalidInput;
4236
4237 for (int i=0; i < m_maxLength; ++i) {
4238 if (m_maskData[i].separator) {
4239 if (str.at(i) != m_maskData[i].maskChar)
4240 return InvalidInput;
4241 } else {
4242 if (!isValidInput(str.at(i), m_maskData[i].maskChar))
4243 return InvalidInput;
4244 }
4245 }
4246 return AcceptableInput;
4247}
4248
4249/*!
4250 \internal
4251
4252 Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
4253 specifies from where characters should be gotten when a separator is met in \a str - true means
4254 that blanks will be used, false that previous input is used.
4255 Calling this when no inputMask is set is undefined.
4256*/
4257QString QQuickTextInputPrivate::maskString(uint pos, const QString &str, bool clear) const
4258{
4259 if (pos >= (uint)m_maxLength)
4260 return QString::fromLatin1("");
4261
4262 QString fill;
4263 fill = clear ? clearString(0, m_maxLength) : m_text;
4264
4265 int strIndex = 0;
4266 QString s = QString::fromLatin1("");
4267 int i = pos;
4268 while (i < m_maxLength) {
4269 if (strIndex < str.size()) {
4270 if (m_maskData[i].separator) {
4271 s += m_maskData[i].maskChar;
4272 if (str[strIndex] == m_maskData[i].maskChar)
4273 strIndex++;
4274 ++i;
4275 } else {
4276 if (isValidInput(str[strIndex], m_maskData[i].maskChar)) {
4277 switch (m_maskData[i].caseMode) {
4278 case MaskInputData::Upper:
4279 s += str[strIndex].toUpper();
4280 break;
4281 case MaskInputData::Lower:
4282 s += str[strIndex].toLower();
4283 break;
4284 default:
4285 s += str[strIndex];
4286 }
4287 ++i;
4288 } else {
4289 // search for separator first
4290 int n = findInMask(i, true, true, str[strIndex]);
4291 if (n != -1) {
4292 if (str.size() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[strIndex]))) {
4293 s += QStringView{fill}.mid(i, n-i+1);
4294 i = n + 1; // update i to find + 1
4295 }
4296 } else {
4297 // search for valid m_blank if not
4298 n = findInMask(i, true, false, str[strIndex]);
4299 if (n != -1) {
4300 s += QStringView{fill}.mid(i, n-i);
4301 switch (m_maskData[n].caseMode) {
4302 case MaskInputData::Upper:
4303 s += str[strIndex].toUpper();
4304 break;
4305 case MaskInputData::Lower:
4306 s += str[strIndex].toLower();
4307 break;
4308 default:
4309 s += str[strIndex];
4310 }
4311 i = n + 1; // updates i to find + 1
4312 }
4313 }
4314 }
4315 ++strIndex;
4316 }
4317 } else
4318 break;
4319 }
4320
4321 return s;
4322}
4323
4324
4325
4326/*!
4327 \internal
4328
4329 Returns a "cleared" string with only separators and blank chars.
4330 Calling this when no inputMask is set is undefined.
4331*/
4332QString QQuickTextInputPrivate::clearString(uint pos, uint len) const
4333{
4334 if (pos >= (uint)m_maxLength)
4335 return QString();
4336
4337 QString s;
4338 int end = qMin((uint)m_maxLength, pos + len);
4339 for (int i = pos; i < end; ++i)
4340 if (m_maskData[i].separator)
4341 s += m_maskData[i].maskChar;
4342 else
4343 s += m_blank;
4344
4345 return s;
4346}
4347
4348/*!
4349 \internal
4350
4351 Strips blank parts of the input in a QQuickTextInputPrivate when an inputMask is set,
4352 separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
4353*/
4354QString QQuickTextInputPrivate::stripString(const QString &str) const
4355{
4356 if (!m_maskData)
4357 return str;
4358
4359 QString s;
4360 int end = qMin(m_maxLength, str.size());
4361 for (int i = 0; i < end; ++i) {
4362 if (m_maskData[i].separator)
4363 s += m_maskData[i].maskChar;
4364 else if (str[i] != m_blank)
4365 s += str[i];
4366 }
4367
4368 return s;
4369}
4370
4371/*!
4372 \internal
4373 searches forward/backward in m_maskData for either a separator or a m_blank
4374*/
4375int QQuickTextInputPrivate::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
4376{
4377 if (pos >= m_maxLength || pos < 0)
4378 return -1;
4379
4380 int end = forward ? m_maxLength : -1;
4381 int step = forward ? 1 : -1;
4382 int i = pos;
4383
4384 while (i != end) {
4385 if (findSeparator) {
4386 if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
4387 return i;
4388 } else {
4389 if (!m_maskData[i].separator) {
4390 if (searchChar.isNull())
4391 return i;
4392 else if (isValidInput(searchChar, m_maskData[i].maskChar))
4393 return i;
4394 }
4395 }
4396 i += step;
4397 }
4398 return -1;
4399}
4400
4401void QQuickTextInputPrivate::internalUndo(int until)
4402{
4403 if (!isUndoAvailable())
4404 return;
4405 cancelPasswordEchoTimer();
4406 internalDeselect();
4407 while (m_undoState && m_undoState > until) {
4408 Command& cmd = m_history[--m_undoState];
4409 switch (cmd.type) {
4410 case Insert:
4411 m_text.remove(cmd.pos, 1);
4412 m_cursor = cmd.pos;
4413 break;
4414 case SetSelection:
4415 m_selstart = cmd.selStart;
4416 m_selend = cmd.selEnd;
4417 m_cursor = cmd.pos;
4418 break;
4419 case Remove:
4420 case RemoveSelection:
4421 m_text.insert(cmd.pos, cmd.uc);
4422 m_cursor = cmd.pos + 1;
4423 break;
4424 case Delete:
4425 case DeleteSelection:
4426 m_text.insert(cmd.pos, cmd.uc);
4427 m_cursor = cmd.pos;
4428 break;
4429 case Separator:
4430 continue;
4431 }
4432 if (until < 0 && m_undoState) {
4433 Command& next = m_history[m_undoState-1];
4434 if (next.type != cmd.type
4435 && next.type < RemoveSelection
4436 && (cmd.type < RemoveSelection || next.type == Separator)) {
4437 break;
4438 }
4439 }
4440 }
4441 separate();
4442 m_textDirty = true;
4443}
4444
4445void QQuickTextInputPrivate::internalRedo()
4446{
4447 if (!isRedoAvailable())
4448 return;
4449 internalDeselect();
4450 while (m_undoState < m_history.size()) {
4451 Command& cmd = m_history[m_undoState++];
4452 switch (cmd.type) {
4453 case Insert:
4454 m_text.insert(cmd.pos, cmd.uc);
4455 m_cursor = cmd.pos + 1;
4456 break;
4457 case SetSelection:
4458 m_selstart = cmd.selStart;
4459 m_selend = cmd.selEnd;
4460 m_cursor = cmd.pos;
4461 break;
4462 case Remove:
4463 case Delete:
4464 case RemoveSelection:
4465 case DeleteSelection:
4466 m_text.remove(cmd.pos, 1);
4467 m_selstart = cmd.selStart;
4468 m_selend = cmd.selEnd;
4469 m_cursor = cmd.pos;
4470 break;
4471 case Separator:
4472 m_selstart = cmd.selStart;
4473 m_selend = cmd.selEnd;
4474 m_cursor = cmd.pos;
4475 break;
4476 }
4477 if (m_undoState < m_history.size()) {
4478 Command& next = m_history[m_undoState];
4479 if (next.type != cmd.type
4480 && cmd.type < RemoveSelection
4481 && next.type != Separator
4482 && (next.type < RemoveSelection || cmd.type == Separator)) {
4483 break;
4484 }
4485 }
4486 }
4487 m_textDirty = true;
4488}
4489
4490void QQuickTextInputPrivate::emitUndoRedoChanged()
4491{
4492 Q_Q(QQuickTextInput);
4493 const bool previousUndo = canUndo;
4494 const bool previousRedo = canRedo;
4495
4496 canUndo = isUndoAvailable();
4497 canRedo = isRedoAvailable();
4498
4499 if (previousUndo != canUndo)
4500 emit q->canUndoChanged();
4501 if (previousRedo != canRedo)
4502 emit q->canRedoChanged();
4503}
4504
4505/*!
4506 \internal
4507
4508 If the current cursor position differs from the last emitted cursor
4509 position, emits cursorPositionChanged().
4510*/
4511bool QQuickTextInputPrivate::emitCursorPositionChanged()
4512{
4513 Q_Q(QQuickTextInput);
4514 if (m_cursor != m_lastCursorPos) {
4515 m_lastCursorPos = m_cursor;
4516
4517 q->updateCursorRectangle();
4518 emit q->cursorPositionChanged();
4519
4520 if (!hasSelectedText()) {
4521 if (lastSelectionStart != m_cursor) {
4522 lastSelectionStart = m_cursor;
4523 emit q->selectionStartChanged();
4524 }
4525 if (lastSelectionEnd != m_cursor) {
4526 lastSelectionEnd = m_cursor;
4527 emit q->selectionEndChanged();
4528 }
4529 }
4530
4531#if QT_CONFIG(accessibility)
4532 if (QAccessible::isActive()) {
4533 if (QObject *acc = QQuickAccessibleAttached::findAccessible(q, QAccessible::EditableText)) {
4534 QAccessibleTextCursorEvent ev(acc, m_cursor);
4535 QAccessible::updateAccessibility(&ev);
4536 }
4537 }
4538#endif
4539
4540 return true;
4541 }
4542 return false;
4543}
4544
4545
4546void QQuickTextInputPrivate::setBlinkingCursorEnabled(bool enable)
4547{
4548 if (enable == m_blinkEnabled)
4549 return;
4550
4551 m_blinkEnabled = enable;
4552 updateCursorBlinking();
4553
4554 if (enable)
4555 connect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QQuickTextInputPrivate::updateCursorBlinking);
4556 else
4557 disconnect(qApp->styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QQuickTextInputPrivate::updateCursorBlinking);
4558}
4559
4560void QQuickTextInputPrivate::updateCursorBlinking()
4561{
4562 Q_Q(QQuickTextInput);
4563
4564 if (m_blinkTimer) {
4565 q->killTimer(m_blinkTimer);
4566 m_blinkTimer = 0;
4567 }
4568
4569 if (m_blinkEnabled && cursorVisible && !cursorItem && !m_readOnly) {
4570 int flashTime = QGuiApplication::styleHints()->cursorFlashTime();
4571 if (flashTime >= 2)
4572 m_blinkTimer = q->startTimer(flashTime / 2);
4573 }
4574
4575 m_blinkStatus = 1;
4576 updateType = UpdatePaintNode;
4577 q->polish();
4578 q->update();
4579}
4580
4581void QQuickTextInput::timerEvent(QTimerEvent *event)
4582{
4583 Q_D(QQuickTextInput);
4584 if (event->timerId() == d->m_blinkTimer) {
4585 d->m_blinkStatus = !d->m_blinkStatus;
4586 d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
4587 polish();
4588 update();
4589 } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
4590 d->m_passwordEchoTimer.stop();
4591 d->updateDisplayText();
4592 updateCursorRectangle();
4593 }
4594}
4595
4596void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
4597{
4598 Q_Q(QQuickTextInput);
4599
4600 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
4601 if (hasAcceptableInput(m_text) == AcceptableInput || fixup()) {
4602
4603 QInputMethod *inputMethod = QGuiApplication::inputMethod();
4604 inputMethod->commit();
4605
4606 if (activeFocus) {
4607 // If we lost focus after hiding the virtual keyboard, we've already emitted
4608 // editingFinished from handleFocusEvent. Otherwise we emit it now.
4609 emit q->editingFinished();
4610 }
4611
4612 emit q->accepted();
4613 }
4614 event->ignore();
4615 return;
4616 }
4617
4618 if (m_blinkEnabled)
4619 updateCursorBlinking();
4620
4621 if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit
4622 && !m_passwordEchoEditing
4623 && !m_readOnly
4624 && !event->text().isEmpty()
4625 && !(event->modifiers() & Qt::ControlModifier)) {
4626 // Clear the edit and reset to normal echo mode while editing; the
4627 // echo mode switches back when the edit loses focus
4628 // ### resets current content. dubious code; you can
4629 // navigate with keys up, down, back, and select(?), but if you press
4630 // "left" or "right" it clears?
4631 updatePasswordEchoEditing(true);
4632 clear();
4633 }
4634
4635 bool unknown = false;
4636#if QT_CONFIG(shortcut)
4637 bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
4638#endif
4639
4640 if (false) {
4641 }
4642#if QT_CONFIG(shortcut)
4643 else if (event == QKeySequence::Undo) {
4644 q->undo();
4645 }
4646 else if (event == QKeySequence::Redo) {
4647 q->redo();
4648 }
4649 else if (event == QKeySequence::SelectAll) {
4650 selectAll();
4651 }
4652#if QT_CONFIG(clipboard)
4653 else if (event == QKeySequence::Copy) {
4654 copy();
4655 }
4656 else if (event == QKeySequence::Paste) {
4657 if (!m_readOnly) {
4658 QClipboard::Mode mode = QClipboard::Clipboard;
4659 paste(mode);
4660 }
4661 }
4662 else if (event == QKeySequence::Cut) {
4663 q->cut();
4664 }
4665 else if (event == QKeySequence::DeleteEndOfLine) {
4666 if (!m_readOnly)
4667 deleteEndOfLine();
4668 }
4669#endif // clipboard
4670 else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
4671 home(0);
4672 }
4673 else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
4674 end(0);
4675 }
4676 else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
4677 home(1);
4678 }
4679 else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
4680 end(1);
4681 }
4682 else if (event == QKeySequence::MoveToNextChar) {
4683 if (hasSelectedText()) {
4684 moveCursor(selectionEnd(), false);
4685 } else {
4686 cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
4687 }
4688 }
4689 else if (event == QKeySequence::SelectNextChar) {
4690 cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
4691 }
4692 else if (event == QKeySequence::MoveToPreviousChar) {
4693 if (hasSelectedText()) {
4694 moveCursor(selectionStart(), false);
4695 } else {
4696 cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
4697 }
4698 }
4699 else if (event == QKeySequence::SelectPreviousChar) {
4700 cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
4701 }
4702 else if (event == QKeySequence::MoveToNextWord) {
4703 if (m_echoMode == QQuickTextInput::Normal)
4704 layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
4705 else
4706 layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
4707 }
4708 else if (event == QKeySequence::MoveToPreviousWord) {
4709 if (m_echoMode == QQuickTextInput::Normal)
4710 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
4711 else if (!m_readOnly) {
4712 layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
4713 }
4714 }
4715 else if (event == QKeySequence::SelectNextWord) {
4716 if (m_echoMode == QQuickTextInput::Normal)
4717 layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
4718 else
4719 layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
4720 }
4721 else if (event == QKeySequence::SelectPreviousWord) {
4722 if (m_echoMode == QQuickTextInput::Normal)
4723 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
4724 else
4725 layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
4726 }
4727 else if (event == QKeySequence::Delete) {
4728 if (!m_readOnly)
4729 del();
4730 }
4731 else if (event == QKeySequence::DeleteEndOfWord) {
4732 if (!m_readOnly)
4733 deleteEndOfWord();
4734 }
4735 else if (event == QKeySequence::DeleteStartOfWord) {
4736 if (!m_readOnly)
4737 deleteStartOfWord();
4738 } else if (event == QKeySequence::DeleteCompleteLine) {
4739 if (!m_readOnly) {
4740 selectAll();
4741#if QT_CONFIG(clipboard)
4742 copy();
4743#endif
4744 del();
4745 }
4746 }
4747#endif // shortcut
4748 else {
4749 bool handled = false;
4750 if (event->modifiers() & Qt::ControlModifier) {
4751 switch (event->key()) {
4752 case Qt::Key_Backspace:
4753 if (!m_readOnly)
4754 deleteStartOfWord();
4755 break;
4756 default:
4757 if (!handled)
4758 unknown = true;
4759 }
4760 } else { // ### check for *no* modifier
4761 switch (event->key()) {
4762 case Qt::Key_Backspace:
4763 if (!m_readOnly) {
4764 backspace();
4765 }
4766 break;
4767 default:
4768 if (!handled)
4769 unknown = true;
4770 }
4771 }
4772 }
4773
4774 if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
4775 setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
4776 unknown = false;
4777 }
4778
4779 if (unknown && !m_readOnly) {
4780 if (m_inputControl->isAcceptableInput(event)) {
4781 if (overwriteMode
4782 // no need to call del() if we have a selection, insert
4783 // does it already
4784 && !hasSelectedText()
4785 && !(m_cursor == q_func()->text().size())) {
4786 del();
4787 }
4788
4789 insert(event->text());
4790 event->accept();
4791 return;
4792 }
4793 }
4794
4795 if (unknown)
4796 event->ignore();
4797 else
4798 event->accept();
4799}
4800
4801/*!
4802 \internal
4803
4804 Deletes the portion of the word before the current cursor position.
4805*/
4806
4807void QQuickTextInputPrivate::deleteStartOfWord()
4808{
4809 int priorState = m_undoState;
4810
4811 if (!separateSelection()) {
4812 Command cmd(SetSelection, m_cursor, u'\0', m_selstart, m_selend);
4813 separate();
4814 cursorWordBackward(true);
4815 addCommand(cmd);
4816 }
4817
4818 removeSelectedText();
4819 finishChange(priorState);
4820}
4821
4822/*!
4823 \internal
4824
4825 Deletes the portion of the word after the current cursor position.
4826*/
4827
4828void QQuickTextInputPrivate::deleteEndOfWord()
4829{
4830 int priorState = m_undoState;
4831 Command cmd(SetSelection, m_cursor, u'\0', m_selstart, m_selend);
4832 separate();
4833 cursorWordForward(true);
4834 // moveCursor (sometimes) calls separate() so we need to add the command after that so the
4835 // cursor position and selection are restored in the same undo operation as the remove.
4836 addCommand(cmd);
4837 removeSelectedText();
4838 finishChange(priorState);
4839}
4840
4841/*!
4842 \internal
4843
4844 Deletes all text from the cursor position to the end of the line.
4845*/
4846
4847void QQuickTextInputPrivate::deleteEndOfLine()
4848{
4849 int priorState = m_undoState;
4850 Command cmd(SetSelection, m_cursor, u'\0', m_selstart, m_selend);
4851 separate();
4852 setSelection(m_cursor, end());
4853 addCommand(cmd);
4854 removeSelectedText();
4855 finishChange(priorState);
4856}
4857
4858/*!
4859 \qmlmethod void QtQuick::TextInput::ensureVisible(int position)
4860 \since 5.4
4861
4862 Scrolls the contents of the text input so that the specified character
4863 \a position is visible inside the boundaries of the text input.
4864
4865 \sa autoScroll
4866*/
4867void QQuickTextInput::ensureVisible(int position)
4868{
4869 Q_D(QQuickTextInput);
4870 d->ensureVisible(position);
4871 updateCursorRectangle(false);
4872}
4873
4874/*!
4875 \qmlmethod void QtQuick::TextInput::clear()
4876 \since 5.7
4877
4878 Clears the contents of the text input
4879 and resets partial text input from an input method.
4880
4881 Use this method instead of setting the \l text property to an empty string.
4882
4883 \sa QInputMethod::reset()
4884*/
4885void QQuickTextInput::clear()
4886{
4887 Q_D(QQuickTextInput);
4888 d->cancelInput();
4889 d->clear();
4890}
4891
4892/*!
4893 \since 5.6
4894 \qmlproperty real QtQuick::TextInput::padding
4895 \qmlproperty real QtQuick::TextInput::topPadding
4896 \qmlproperty real QtQuick::TextInput::leftPadding
4897 \qmlproperty real QtQuick::TextInput::bottomPadding
4898 \qmlproperty real QtQuick::TextInput::rightPadding
4899
4900 These properties hold the padding around the content. This space is reserved
4901 in addition to the contentWidth and contentHeight.
4902
4903 The individual padding properties assume the value of the \c padding
4904 property unless they are set explicitly. For example, if \c padding is
4905 set to \c 4 and \c leftPadding to \c 8, \c 8 will be used as the left
4906 padding.
4907
4908 \note If an explicit width or height is given to a TextInput, care must be
4909 taken to ensure it is large enough to accommodate the relevant padding
4910 values. For example: if \c topPadding and \c bottomPadding are set to
4911 \c 10, but the height of the TextInput is only set to \c 20, the text will
4912 not have enough vertical space in which to be rendered, and will appear
4913 clipped.
4914*/
4915qreal QQuickTextInput::padding() const
4916{
4917 Q_D(const QQuickTextInput);
4918 return d->padding();
4919}
4920
4921void QQuickTextInput::setPadding(qreal padding)
4922{
4923 Q_D(QQuickTextInput);
4924 if (qFuzzyCompare(d->padding(), padding))
4925 return;
4926
4927 d->extra.value().padding = padding;
4928 d->updateLayout();
4929 updateCursorRectangle();
4930 emit paddingChanged();
4931 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
4932 emit topPaddingChanged();
4933 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
4934 emit leftPaddingChanged();
4935 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
4936 emit rightPaddingChanged();
4937 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
4938 emit bottomPaddingChanged();
4939}
4940
4941void QQuickTextInput::resetPadding()
4942{
4943 setPadding(0);
4944}
4945
4946qreal QQuickTextInput::topPadding() const
4947{
4948 Q_D(const QQuickTextInput);
4949 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
4950 return d->extra->topPadding;
4951 return d->padding();
4952}
4953
4954void QQuickTextInput::setTopPadding(qreal padding)
4955{
4956 Q_D(QQuickTextInput);
4957 d->setTopPadding(padding);
4958}
4959
4960void QQuickTextInput::resetTopPadding()
4961{
4962 Q_D(QQuickTextInput);
4963 d->setTopPadding(0, true);
4964}
4965
4966qreal QQuickTextInput::leftPadding() const
4967{
4968 Q_D(const QQuickTextInput);
4969 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
4970 return d->extra->leftPadding;
4971 return d->padding();
4972}
4973
4974void QQuickTextInput::setLeftPadding(qreal padding)
4975{
4976 Q_D(QQuickTextInput);
4977 d->setLeftPadding(padding);
4978}
4979
4980void QQuickTextInput::resetLeftPadding()
4981{
4982 Q_D(QQuickTextInput);
4983 d->setLeftPadding(0, true);
4984}
4985
4986qreal QQuickTextInput::rightPadding() const
4987{
4988 Q_D(const QQuickTextInput);
4989 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
4990 return d->extra->rightPadding;
4991 return d->padding();
4992}
4993
4994void QQuickTextInput::setRightPadding(qreal padding)
4995{
4996 Q_D(QQuickTextInput);
4997 d->setRightPadding(padding);
4998}
4999
5000void QQuickTextInput::resetRightPadding()
5001{
5002 Q_D(QQuickTextInput);
5003 d->setRightPadding(0, true);
5004}
5005
5006qreal QQuickTextInput::bottomPadding() const
5007{
5008 Q_D(const QQuickTextInput);
5009 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
5010 return d->extra->bottomPadding;
5011 return d->padding();
5012}
5013
5014void QQuickTextInput::setBottomPadding(qreal padding)
5015{
5016 Q_D(QQuickTextInput);
5017 d->setBottomPadding(padding);
5018}
5019
5020void QQuickTextInput::resetBottomPadding()
5021{
5022 Q_D(QQuickTextInput);
5023 d->setBottomPadding(0, true);
5024}
5025
5026#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
5027void QQuickTextInput::setOldSelectionDefault()
5028{
5029 Q_D(QQuickTextInput);
5030 d->selectByMouse = false;
5031 d->selectByTouchDrag = true;
5032 qCDebug(lcQuickTextInput, "pre-6.4 behavior chosen: selectByMouse defaults false; if enabled, touchscreen acts like a mouse");
5033}
5034
5035// TODO in 6.7.0: remove the note about versions prior to 6.4 in selectByMouse() documentation
5036QQuickPre64TextInput::QQuickPre64TextInput(QQuickItem *parent)
5037 : QQuickTextInput(parent)
5038{
5039 setOldSelectionDefault();
5040}
5041#endif
5042
5043QT_END_NAMESPACE
5044
5045#include "moc_qquicktextinput_p.cpp"
Combined button and popup list for selecting options.