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

source code of qtdeclarative/src/quick/items/qquicktextinput.cpp