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

source code of qtdeclarative/src/quick/items/qquicktextinput.cpp