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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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