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