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