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