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