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