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(content) : content;
136 return (res.isNull() ? QString::fromLatin1("") : 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(s, -1, 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(start, end);
221
222 return d->m_text.mid(start, end - start);
223}
224
225QString QQuickTextInputPrivate::realText() const
226{
227 QString res = m_maskData ? stripString(m_text) : m_text;
228 return (res.isNull() ? QString::fromLatin1("") : 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->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(Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle);
437#endif
438 }
439 emit fontChanged(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(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(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(QQuickItem::ItemAcceptsInputMethod, !ro);
744#endif
745 d->m_readOnly = ro;
746 d->setCursorPosition(d->end());
747#if QT_CONFIG(im)
748 updateInputMethod(Qt::ImEnabled);
749#endif
750 q_canPasteChanged();
751 d->emitUndoRedoChanged();
752 emit readOnlyChanged(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(d->m_text, -1, false);
783
784 emit maximumLengthChanged(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(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(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(c);
871 if (!l.isValid())
872 return QRectF();
873 qreal x = l.cursorToX(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(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, 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(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(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(a);
1098 if (l.isValid()) {
1099 qreal x = l.cursorToX(a) - hscroll + q->leftPadding();
1100 qreal y = l.y() - vscroll + q->topPadding();
1101 rect.setRect(x, y, 1, l.height());
1102 }
1103 }
1104 return rect;
1105}
1106
1107void QQuickTextInputPrivate::checkIsValid()
1108{
1109 Q_Q(QQuickTextInput);
1110
1111 ValidatorState state = hasAcceptableInput(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 if (d->inputMask() == im)
1144 return;
1145
1146 d->setInputMask(im);
1147 emit inputMaskChanged(d->inputMask());
1148}
1149
1150/*!
1151 \qmlproperty bool QtQuick::TextInput::acceptableInput
1152
1153 This property is always true unless a validator or input mask has been set.
1154 If a validator or input mask has been set, this property will only be true
1155 if the current text is acceptable to the validator or input mask as a final
1156 string (not as an intermediate string).
1157*/
1158bool QQuickTextInput::hasAcceptableInput() const
1159{
1160 Q_D(const QQuickTextInput);
1161 return d->m_acceptableInput;
1162}
1163
1164/*!
1165 \qmlsignal QtQuick::TextInput::accepted()
1166
1167 This signal is emitted when the Return or Enter key is pressed.
1168 Note that if there is a \l validator or \l inputMask set on the text
1169 input, the signal will only be emitted if the input is in an acceptable
1170 state.
1171
1172 The corresponding handler is \c onAccepted.
1173*/
1174
1175/*!
1176 \qmlsignal QtQuick::TextInput::editingFinished()
1177 \since 5.2
1178
1179 This signal is emitted when the Return or Enter key is pressed or
1180 the text input loses focus. Note that if there is a validator or
1181 inputMask set on the text input and enter/return is pressed, this
1182 signal will only be emitted if the input follows
1183 the inputMask and the validator returns an acceptable state.
1184
1185 The corresponding handler is \c onEditingFinished.
1186*/
1187
1188/*!
1189 \qmlsignal QtQuick::TextInput::textEdited()
1190 \since 5.9
1191
1192 This signal is emitted whenever the text is edited. Unlike \c textChanged(),
1193 this signal is not emitted when the text is changed programmatically, for example,
1194 by changing the value of the \c text property or by calling \c clear().
1195
1196 The corresponding handler is \c onTextEdited.
1197*/
1198
1199#if QT_CONFIG(im)
1200Qt::InputMethodHints QQuickTextInputPrivate::effectiveInputMethodHints() const
1201{
1202 Qt::InputMethodHints hints = inputMethodHints;
1203 if (m_echoMode == QQuickTextInput::Password || m_echoMode == QQuickTextInput::NoEcho)
1204 hints |= Qt::ImhHiddenText;
1205 else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
1206 hints &= ~Qt::ImhHiddenText;
1207 if (m_echoMode != QQuickTextInput::Normal)
1208 hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
1209 return hints;
1210}
1211#endif
1212
1213/*!
1214 \qmlproperty enumeration QtQuick::TextInput::echoMode
1215
1216 Specifies how the text should be displayed in the TextInput.
1217 \list
1218 \li TextInput.Normal - Displays the text as it is. (Default)
1219 \li TextInput.Password - Displays platform-dependent password mask
1220 characters instead of the actual characters.
1221 \li TextInput.NoEcho - Displays nothing.
1222 \li TextInput.PasswordEchoOnEdit - Displays characters as they are entered
1223 while editing, otherwise identical to \c TextInput.Password.
1224 \endlist
1225*/
1226QQuickTextInput::EchoMode QQuickTextInput::echoMode() const
1227{
1228 Q_D(const QQuickTextInput);
1229 return QQuickTextInput::EchoMode(d->m_echoMode);
1230}
1231
1232void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
1233{
1234 Q_D(QQuickTextInput);
1235 if (echoMode() == echo)
1236 return;
1237 d->cancelPasswordEchoTimer();
1238 d->m_echoMode = echo;
1239 d->m_passwordEchoEditing = false;
1240#if QT_CONFIG(im)
1241 updateInputMethod(Qt::ImHints);
1242#endif
1243 d->updateDisplayText();
1244 updateCursorRectangle();
1245
1246 // If this control is used for password input, we want to minimize
1247 // the possibility of string reallocation not to leak (parts of)
1248 // the password.
1249 if (d->m_echoMode != QQuickTextInput::Normal)
1250 d->m_text.reserve(30);
1251
1252 emit echoModeChanged(echoMode());
1253}
1254
1255/*!
1256 \qmlproperty enumeration QtQuick::TextInput::inputMethodHints
1257
1258 Provides hints to the input method about the expected content of the text input and how it
1259 should operate.
1260
1261 The value is a bit-wise combination of flags, or Qt.ImhNone if no hints are set.
1262
1263 Flags that alter behaviour are:
1264
1265 \list
1266 \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
1267 This is automatically set when setting echoMode to \c TextInput.Password.
1268 \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
1269 in any persistent storage like predictive user dictionary.
1270 \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
1271 when a sentence ends.
1272 \li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
1273 \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
1274 \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
1275 \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
1276
1277 \li Qt.ImhDate - The text editor functions as a date field.
1278 \li Qt.ImhTime - The text editor functions as a time field.
1279 \li Qt.ImhMultiLine - The text editor doesn't close software input keyboard when Return or Enter key is pressed (since QtQuick 2.4).
1280 \endlist
1281
1282 Flags that restrict input (exclusive flags) are:
1283
1284 \list
1285 \li Qt.ImhDigitsOnly - Only digits are allowed.
1286 \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
1287 \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
1288 \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
1289 \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
1290 \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
1291 \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
1292 \endlist
1293
1294 Masks:
1295
1296 \list
1297 \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
1298 \endlist
1299*/
1300
1301Qt::InputMethodHints QQuickTextInput::inputMethodHints() const
1302{
1303#if !QT_CONFIG(im)
1304 return Qt::ImhNone;
1305#else
1306 Q_D(const QQuickTextInput);
1307 return d->inputMethodHints;
1308#endif // im
1309}
1310
1311void QQuickTextInput::setInputMethodHints(Qt::InputMethodHints hints)
1312{
1313#if !QT_CONFIG(im)
1314 Q_UNUSED(hints);
1315#else
1316 Q_D(QQuickTextInput);
1317
1318 if (hints == d->inputMethodHints)
1319 return;
1320
1321 d->inputMethodHints = hints;
1322 updateInputMethod(Qt::ImHints);
1323 emit inputMethodHintsChanged();
1324#endif // im
1325}
1326
1327/*!
1328 \qmlproperty Component QtQuick::TextInput::cursorDelegate
1329 The delegate for the cursor in the TextInput.
1330
1331 If you set a cursorDelegate for a TextInput, this delegate will be used for
1332 drawing the cursor instead of the standard cursor. An instance of the
1333 delegate will be created and managed by the TextInput when a cursor is
1334 needed, and the x property of the delegate instance will be set so as
1335 to be one pixel before the top left of the current character.
1336
1337 Note that the root item of the delegate component must be a QQuickItem or
1338 QQuickItem derived item.
1339*/
1340QQmlComponent* QQuickTextInput::cursorDelegate() const
1341{
1342 Q_D(const QQuickTextInput);
1343 return d->cursorComponent;
1344}
1345
1346void QQuickTextInput::setCursorDelegate(QQmlComponent* c)
1347{
1348 Q_D(QQuickTextInput);
1349 QQuickTextUtil::setCursorDelegate(d, c);
1350}
1351
1352void QQuickTextInput::createCursor()
1353{
1354 Q_D(QQuickTextInput);
1355 d->cursorPending = true;
1356 QQuickTextUtil::createCursor(d);
1357}
1358
1359/*!
1360 \qmlmethod rect QtQuick::TextInput::positionToRectangle(int pos)
1361
1362 This function takes a character position and returns the rectangle that the
1363 cursor would occupy, if it was placed at that character position.
1364
1365 This is similar to setting the cursorPosition, and then querying the cursor
1366 rectangle, but the cursorPosition is not changed.
1367*/
1368QRectF QQuickTextInput::positionToRectangle(int pos) const
1369{
1370 Q_D(const QQuickTextInput);
1371 if (d->m_echoMode == NoEcho)
1372 pos = 0;
1373#if QT_CONFIG(im)
1374 else if (pos > d->m_cursor)
1375 pos += d->preeditAreaText().length();
1376#endif
1377 QTextLine l = d->m_textLayout.lineForTextPosition(pos);
1378 if (!l.isValid())
1379 return QRectF();
1380 qreal x = l.cursorToX(pos) - d->hscroll;
1381 qreal y = l.y() - d->vscroll;
1382 qreal w = 1;
1383 if (d->overwriteMode) {
1384 if (pos < text().length())
1385 w = l.cursorToX(pos + 1) - x;
1386 else
1387 w = QFontMetrics(font()).horizontalAdvance(QLatin1Char(' ')); // in sync with QTextLine::draw()
1388 }
1389 return QRectF(x, y, w, l.height());
1390}
1391
1392/*!
1393 \qmlmethod int QtQuick::TextInput::positionAt(real x, real y, CursorPosition position = CursorBetweenCharacters)
1394
1395 This function returns the character position at
1396 x and y pixels from the top left of the textInput. Position 0 is before the
1397 first character, position 1 is after the first character but before the second,
1398 and so on until position text.length, which is after all characters.
1399
1400 This means that for all x values before the first character this function returns 0,
1401 and for all x values after the last character this function returns text.length. If
1402 the y value is above the text the position will be that of the nearest character on
1403 the first line and if it is below the text the position of the nearest character
1404 on the last line will be returned.
1405
1406 The cursor position type specifies how the cursor position should be resolved.
1407
1408 \list
1409 \li TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x.
1410 \li TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
1411 \endlist
1412*/
1413
1414void QQuickTextInput::positionAt(QQmlV4Function *args) const
1415{
1416 Q_D(const QQuickTextInput);
1417
1418 qreal x = 0;
1419 qreal y = 0;
1420 QTextLine::CursorPosition position = QTextLine::CursorBetweenCharacters;
1421
1422 if (args->length() < 1)
1423 return;
1424
1425 int i = 0;
1426 QV4::Scope scope(args->v4engine());
1427 QV4::ScopedValue arg(scope, (*args)[0]);
1428 x = arg->toNumber();
1429
1430 if (++i < args->length()) {
1431 arg = (*args)[i];
1432 y = arg->toNumber();
1433 }
1434
1435 if (++i < args->length()) {
1436 arg = (*args)[i];
1437 position = QTextLine::CursorPosition(arg->toInt32());
1438 }
1439
1440 int pos = d->positionAt(x, y, position);
1441 const int cursor = d->m_cursor;
1442 if (pos > cursor) {
1443#if QT_CONFIG(im)
1444 const int preeditLength = d->preeditAreaText().length();
1445 pos = pos > cursor + preeditLength
1446 ? pos - preeditLength
1447 : cursor;
1448#else
1449 pos = cursor;
1450#endif
1451 }
1452 args->setReturnValue(QV4::Encode(pos));
1453}
1454
1455int QQuickTextInputPrivate::positionAt(qreal x, qreal y, QTextLine::CursorPosition position) const
1456{
1457 Q_Q(const QQuickTextInput);
1458 x += hscroll - q->leftPadding();
1459 y += vscroll - q->topPadding();
1460 QTextLine line = m_textLayout.lineAt(0);
1461 for (int i = 1; i < m_textLayout.lineCount(); ++i) {
1462 QTextLine nextLine = m_textLayout.lineAt(i);
1463
1464 if (y < (line.rect().bottom() + nextLine.y()) / 2)
1465 break;
1466 line = nextLine;
1467 }
1468 return line.isValid() ? line.xToCursor(x, position) : 0;
1469}
1470
1471/*!
1472 \qmlproperty bool QtQuick::TextInput::overwriteMode
1473 \since 5.8
1474
1475 Whether text entered by the user will overwrite existing text.
1476
1477 As with many text editors, the text editor widget can be configured
1478 to insert or overwrite existing text with new text entered by the user.
1479
1480 If this property is \c true, existing text is overwritten, character-for-character
1481 by new text; otherwise, text is inserted at the cursor position, displacing
1482 existing text.
1483
1484 By default, this property is \c false (new text does not overwrite existing text).
1485*/
1486bool QQuickTextInput::overwriteMode() const
1487{
1488 Q_D(const QQuickTextInput);
1489 return d->overwriteMode;
1490}
1491
1492void QQuickTextInput::setOverwriteMode(bool overwrite)
1493{
1494 Q_D(QQuickTextInput);
1495 if (d->overwriteMode == overwrite)
1496 return;
1497 d->overwriteMode = overwrite;
1498 emit overwriteModeChanged(overwrite);
1499}
1500
1501void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
1502{
1503 Q_D(QQuickTextInput);
1504 // Don't allow MacOSX up/down support, and we don't allow a completer.
1505 bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier;
1506 if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
1507 // Ignore when moving off the end unless there is a selection,
1508 // because then moving will do something (deselect).
1509 int cursorPosition = d->m_cursor;
1510 if (cursorPosition == 0)
1511 ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
1512 if (!ignore && cursorPosition == d->m_text.length())
1513 ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
1514 }
1515 if (ignore) {
1516 ev->ignore();
1517 } else {
1518 d->processKeyEvent(ev);
1519 }
1520 if (!ev->isAccepted())
1521 QQuickImplicitSizeItem::keyPressEvent(ev);
1522}
1523
1524#if QT_CONFIG(im)
1525void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
1526{
1527 Q_D(QQuickTextInput);
1528 const bool wasComposing = d->hasImState;
1529 if (d->m_readOnly) {
1530 ev->ignore();
1531 } else {
1532 d->processInputMethodEvent(ev);
1533 }
1534 if (!ev->isAccepted())
1535 QQuickImplicitSizeItem::inputMethodEvent(ev);
1536
1537 if (wasComposing != d->hasImState)
1538 emit inputMethodComposingChanged();
1539}
1540#endif
1541
1542void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event)
1543{
1544 Q_D(QQuickTextInput);
1545
1546 if (d->selectByMouse && event->button() == Qt::LeftButton) {
1547#if QT_CONFIG(im)
1548 d->commitPreedit();
1549#endif
1550 int cursor = d->positionAt(event->localPos());
1551 d->selectWordAtPos(cursor);
1552 event->setAccepted(true);
1553 if (!d->hasPendingTripleClick()) {
1554 d->tripleClickStartPoint = event->localPos();
1555 d->tripleClickTimer.start();
1556 }
1557 } else {
1558 if (d->sendMouseEventToInputContext(event))
1559 return;
1560 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
1561 }
1562}
1563
1564void QQuickTextInput::mousePressEvent(QMouseEvent *event)
1565{
1566 Q_D(QQuickTextInput);
1567
1568 d->pressPos = event->localPos();
1569
1570 if (d->sendMouseEventToInputContext(event))
1571 return;
1572
1573 if (d->selectByMouse) {
1574 setKeepMouseGrab(false);
1575 d->selectPressed = true;
1576 QPointF distanceVector = d->pressPos - d->tripleClickStartPoint;
1577 if (d->hasPendingTripleClick()
1578 && distanceVector.manhattanLength() < QGuiApplication::styleHints()->startDragDistance()) {
1579 event->setAccepted(true);
1580 selectAll();
1581 return;
1582 }
1583 }
1584
1585 bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
1586 int cursor = d->positionAt(event->localPos());
1587 d->moveCursor(cursor, mark);
1588
1589 if (d->focusOnPress && !qGuiApp->styleHints()->setFocusOnTouchRelease())
1590 ensureActiveFocus();
1591
1592 event->setAccepted(true);
1593}
1594
1595void QQuickTextInput::mouseMoveEvent(QMouseEvent *event)
1596{
1597 Q_D(QQuickTextInput);
1598
1599 if (d->selectPressed) {
1600 if (qAbs(int(event->localPos().x() - d->pressPos.x())) > QGuiApplication::styleHints()->startDragDistance())
1601 setKeepMouseGrab(true);
1602
1603#if QT_CONFIG(im)
1604 if (d->composeMode()) {
1605 // start selection
1606 int startPos = d->positionAt(d->pressPos);
1607 int currentPos = d->positionAt(event->localPos());
1608 if (startPos != currentPos)
1609 d->setSelection(startPos, currentPos - startPos);
1610 } else
1611#endif
1612 {
1613 moveCursorSelection(d->positionAt(event->localPos()), d->mouseSelectionMode);
1614 }
1615 event->setAccepted(true);
1616 } else {
1617 QQuickImplicitSizeItem::mouseMoveEvent(event);
1618 }
1619}
1620
1621void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
1622{
1623 Q_D(QQuickTextInput);
1624 if (d->sendMouseEventToInputContext(event))
1625 return;
1626 if (d->selectPressed) {
1627 d->selectPressed = false;
1628 setKeepMouseGrab(false);
1629 }
1630#if QT_CONFIG(clipboard)
1631 if (QGuiApplication::clipboard()->supportsSelection()) {
1632 if (event->button() == Qt::LeftButton) {
1633 d->copy(QClipboard::Selection);
1634 } else if (!d->m_readOnly && event->button() == Qt::MidButton) {
1635 d->deselect();
1636 d->insert(QGuiApplication::clipboard()->text(QClipboard::Selection));
1637 }
1638 }
1639#endif
1640
1641 if (d->focusOnPress && qGuiApp->styleHints()->setFocusOnTouchRelease())
1642 ensureActiveFocus();
1643
1644 if (!event->isAccepted())
1645 QQuickImplicitSizeItem::mouseReleaseEvent(event);
1646}
1647
1648bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event)
1649{
1650#if QT_CONFIG(im)
1651 if (composeMode()) {
1652 int tmp_cursor = positionAt(event->localPos());
1653 int mousePos = tmp_cursor - m_cursor;
1654 if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().length()) {
1655 if (event->type() == QEvent::MouseButtonRelease) {
1656 QGuiApplication::inputMethod()->invokeAction(QInputMethod::Click, mousePos);
1657 }
1658 return true;
1659 }
1660 }
1661#else
1662 Q_UNUSED(event);
1663#endif
1664
1665 return false;
1666}
1667
1668void QQuickTextInput::mouseUngrabEvent()
1669{
1670 Q_D(QQuickTextInput);
1671 d->selectPressed = false;
1672 setKeepMouseGrab(false);
1673}
1674
1675bool QQuickTextInput::event(QEvent* ev)
1676{
1677#if QT_CONFIG(shortcut)
1678 Q_D(QQuickTextInput);
1679 if (ev->type() == QEvent::ShortcutOverride) {
1680 if (d->m_readOnly)
1681 return false;
1682 QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
1683 if (ke == QKeySequence::Copy
1684 || ke == QKeySequence::Paste
1685 || ke == QKeySequence::Cut
1686 || ke == QKeySequence::Redo
1687 || ke == QKeySequence::Undo
1688 || ke == QKeySequence::MoveToNextWord
1689 || ke == QKeySequence::MoveToPreviousWord
1690 || ke == QKeySequence::MoveToStartOfDocument
1691 || ke == QKeySequence::MoveToEndOfDocument
1692 || ke == QKeySequence::SelectNextWord
1693 || ke == QKeySequence::SelectPreviousWord
1694 || ke == QKeySequence::SelectStartOfLine
1695 || ke == QKeySequence::SelectEndOfLine
1696 || ke == QKeySequence::SelectStartOfBlock
1697 || ke == QKeySequence::SelectEndOfBlock
1698 || ke == QKeySequence::SelectStartOfDocument
1699 || ke == QKeySequence::SelectAll
1700 || ke == QKeySequence::SelectEndOfDocument
1701 || ke == QKeySequence::DeleteCompleteLine) {
1702 ke->accept();
1703 return true;
1704 } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1705 || ke->modifiers() == Qt::KeypadModifier) {
1706 if (ke->key() < Qt::Key_Escape) {
1707 ke->accept();
1708 return true;
1709 } else {
1710 switch (ke->key()) {
1711 case Qt::Key_Delete:
1712 case Qt::Key_Home:
1713 case Qt::Key_End:
1714 case Qt::Key_Backspace:
1715 case Qt::Key_Left:
1716 case Qt::Key_Right:
1717 ke->accept();
1718 return true;
1719 default:
1720 break;
1721 }
1722 }
1723 }
1724 }
1725#endif
1726
1727 return QQuickImplicitSizeItem::event(ev);
1728}
1729
1730void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
1731 const QRectF &oldGeometry)
1732{
1733 Q_D(QQuickTextInput);
1734 if (!d->inLayout) {
1735 if (newGeometry.width() != oldGeometry.width())
1736 d->updateLayout();
1737 else if (newGeometry.height() != oldGeometry.height() && d->vAlign != QQuickTextInput::AlignTop)
1738 d->updateBaselineOffset();
1739 updateCursorRectangle();
1740 }
1741 QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
1742}
1743
1744void QQuickTextInputPrivate::ensureVisible(int position, int preeditCursor, int preeditLength)
1745{
1746 Q_Q(QQuickTextInput);
1747 QTextLine textLine = m_textLayout.lineForTextPosition(position + preeditCursor);
1748 const qreal width = qMax<qreal>(0, q->width() - q->leftPadding() - q->rightPadding());
1749 qreal cix = 0;
1750 qreal widthUsed = 0;
1751 if (textLine.isValid()) {
1752 cix = textLine.cursorToX(position + preeditLength);
1753 const qreal cursorWidth = cix >= 0 ? cix : width - cix;
1754 widthUsed = qMax(textLine.naturalTextWidth(), cursorWidth);
1755 }
1756 int previousScroll = hscroll;
1757
1758 if (widthUsed <= width) {
1759 hscroll = 0;
1760 } else {
1761 Q_ASSERT(textLine.isValid());
1762 if (cix - hscroll >= width) {
1763 // text doesn't fit, cursor is to the right of br (scroll right)
1764 hscroll = cix - width;
1765 } else if (cix - hscroll < 0 && hscroll < widthUsed) {
1766 // text doesn't fit, cursor is to the left of br (scroll left)
1767 hscroll = cix;
1768 } else if (widthUsed - hscroll < width) {
1769 // text doesn't fit, text document is to the left of br; align
1770 // right
1771 hscroll = widthUsed - width;
1772 } else if (width - hscroll > widthUsed) {
1773 // text doesn't fit, text document is to the right of br; align
1774 // left
1775 hscroll = width - widthUsed;
1776 }
1777#if QT_CONFIG(im)
1778 if (preeditLength > 0) {
1779 // check to ensure long pre-edit text doesn't push the cursor
1780 // off to the left
1781 cix = textLine.cursorToX(position + qMax(0, preeditCursor - 1));
1782 if (cix < hscroll)
1783 hscroll = cix;
1784 }
1785#endif
1786 }
1787 if (previousScroll != hscroll)
1788 textLayoutDirty = true;
1789}
1790
1791void QQuickTextInputPrivate::updateHorizontalScroll()
1792{
1793 if (autoScroll && m_echoMode != QQuickTextInput::NoEcho) {
1794#if QT_CONFIG(im)
1795 const int preeditLength = m_textLayout.preeditAreaText().length();
1796 ensureVisible(m_cursor, m_preeditCursor, preeditLength);
1797#else
1798 ensureVisible(m_cursor);
1799#endif
1800 } else {
1801 hscroll = 0;
1802 }
1803}
1804
1805void QQuickTextInputPrivate::updateVerticalScroll()
1806{
1807 Q_Q(QQuickTextInput);
1808#if QT_CONFIG(im)
1809 const int preeditLength = m_textLayout.preeditAreaText().length();
1810#endif
1811 const qreal height = qMax<qreal>(0, q->height() - q->topPadding() - q->bottomPadding());
1812 qreal heightUsed = contentSize.height();
1813 qreal previousScroll = vscroll;
1814
1815 if (!autoScroll || heightUsed <= height) {
1816 // text fits in br; use vscroll for alignment
1817 vscroll = -QQuickTextUtil::alignedY(
1818 heightUsed, height, vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask));
1819 } else {
1820#if QT_CONFIG(im)
1821 QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + preeditLength);
1822#else
1823 QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor);
1824#endif
1825 QRectF r = currentLine.isValid() ? currentLine.rect() : QRectF();
1826 qreal top = r.top();
1827 int bottom = r.bottom();
1828
1829 if (bottom - vscroll >= height) {
1830 // text doesn't fit, cursor is to the below the br (scroll down)
1831 vscroll = bottom - height;
1832 } else if (top - vscroll < 0 && vscroll < heightUsed) {
1833 // text doesn't fit, cursor is above br (scroll up)
1834 vscroll = top;
1835 } else if (heightUsed - vscroll < height) {
1836 // text doesn't fit, text document is to the left of br; align
1837 // right
1838 vscroll = heightUsed - height;
1839 }
1840#if QT_CONFIG(im)
1841 if (preeditLength > 0) {
1842 // check to ensure long pre-edit text doesn't push the cursor
1843 // off the top
1844 currentLine = m_textLayout.lineForTextPosition(m_cursor + qMax(0, m_preeditCursor - 1));
1845 top = currentLine.isValid() ? currentLine.rect().top() : 0;
1846 if (top < vscroll)
1847 vscroll = top;
1848 }
1849#endif
1850 }
1851 if (previousScroll != vscroll)
1852 textLayoutDirty = true;
1853}
1854
1855void QQuickTextInput::triggerPreprocess()
1856{
1857 Q_D(QQuickTextInput);
1858 if (d->updateType == QQuickTextInputPrivate::UpdateNone)
1859 d->updateType = QQuickTextInputPrivate::UpdateOnlyPreprocess;
1860 polish();
1861 update();
1862}
1863
1864void QQuickTextInput::updatePolish()
1865{
1866 invalidateFontCaches();
1867}
1868
1869void QQuickTextInput::invalidateFontCaches()
1870{
1871 Q_D(QQuickTextInput);
1872
1873 if (d->m_textLayout.engine() != nullptr)
1874 d->m_textLayout.engine()->resetFontEngineCache();
1875}
1876
1877void QQuickTextInput::ensureActiveFocus()
1878{
1879 bool hadActiveFocus = hasActiveFocus();
1880 forceActiveFocus();
1881#if QT_CONFIG(im)
1882 Q_D(QQuickTextInput);
1883 // re-open input panel on press if already focused
1884 if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
1885 qGuiApp->inputMethod()->show();
1886#else
1887 Q_UNUSED(hadActiveFocus);
1888#endif
1889}
1890
1891QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1892{
1893 Q_UNUSED(data);
1894 Q_D(QQuickTextInput);
1895
1896 if (d->updateType != QQuickTextInputPrivate::UpdatePaintNode && oldNode != nullptr) {
1897 // Update done in preprocess() in the nodes
1898 d->updateType = QQuickTextInputPrivate::UpdateNone;
1899 return oldNode;
1900 }
1901
1902 d->updateType = QQuickTextInputPrivate::UpdateNone;
1903
1904 QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
1905 if (node == nullptr)
1906 node = new QQuickTextNode(this);
1907 d->textNode = node;
1908
1909 const bool showCursor = !isReadOnly() && d->cursorItem == nullptr && d->cursorVisible && d->m_blinkStatus;
1910
1911 if (!d->textLayoutDirty && oldNode != nullptr) {
1912 if (showCursor)
1913 node->setCursor(cursorRectangle(), d->color);
1914 else
1915 node->clearCursor();
1916 } else {
1917 node->setUseNativeRenderer(d->renderType == NativeRendering);
1918 node->deleteContent();
1919 node->setMatrix(QMatrix4x4());
1920
1921 QPointF offset(leftPadding(), topPadding());
1922 if (d->autoScroll && d->m_textLayout.lineCount() > 0) {
1923 QFontMetricsF fm(d->font);
1924 // the y offset is there to keep the baseline constant in case we have script changes in the text.
1925 offset += -QPointF(d->hscroll, d->vscroll + d->m_textLayout.lineAt(0).ascent() - fm.ascent());
1926 } else {
1927 offset += -QPointF(d->hscroll, d->vscroll);
1928 }
1929
1930 if (!d->m_textLayout.text().isEmpty()
1931#if QT_CONFIG(im)
1932 || !d->m_textLayout.preeditAreaText().isEmpty()
1933#endif
1934 ) {
1935 node->addTextLayout(offset, &d->m_textLayout, d->color,
1936 QQuickText::Normal, QColor(), QColor(),
1937 d->selectionColor, d->selectedTextColor,
1938 d->selectionStart(),
1939 d->selectionEnd() - 1); // selectionEnd() returns first char after
1940 // selection
1941 }
1942
1943 if (showCursor)
1944 node->setCursor(cursorRectangle(), d->color);
1945
1946 d->textLayoutDirty = false;
1947 }
1948
1949 invalidateFontCaches();
1950
1951 return node;
1952}
1953
1954#if QT_CONFIG(im)
1955QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
1956{
1957 return inputMethodQuery(property, QVariant());
1958}
1959
1960QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
1961{
1962 Q_D(const QQuickTextInput);
1963 switch (property) {
1964 case Qt::ImEnabled:
1965 return QVariant((bool)(flags() & ItemAcceptsInputMethod));
1966 case Qt::ImHints:
1967 return QVariant((int) d->effectiveInputMethodHints());
1968 case Qt::ImCursorRectangle:
1969 return cursorRectangle();
1970 case Qt::ImAnchorRectangle:
1971 return d->anchorRectangle();
1972 case Qt::ImFont:
1973 return font();
1974 case Qt::ImCursorPosition: {
1975 const QPointF pt = argument.toPointF();
1976 if (!pt.isNull())
1977 return QVariant(d->positionAt(pt));
1978 return QVariant(d->m_cursor);
1979 }
1980 case Qt::ImSurroundingText:
1981 if (d->m_echoMode == PasswordEchoOnEdit && !d->m_passwordEchoEditing) {
1982 return QVariant(displayText());
1983 } else {
1984 return QVariant(d->realText());
1985 }
1986 case Qt::ImCurrentSelection:
1987 return QVariant(selectedText());
1988 case Qt::ImMaximumTextLength:
1989 return QVariant(maxLength());
1990 case Qt::ImAnchorPosition:
1991 if (d->selectionStart() == d->selectionEnd())
1992 return QVariant(d->m_cursor);
1993 else if (d->selectionStart() == d->m_cursor)
1994 return QVariant(d->selectionEnd());
1995 else
1996 return QVariant(d->selectionStart());
1997 case Qt::ImAbsolutePosition:
1998 return QVariant(d->m_cursor);
1999 case Qt::ImTextAfterCursor:
2000 if (argument.isValid())
2001 return QVariant(d->m_text.mid(d->m_cursor, argument.toInt()));
2002 return QVariant(d->m_text.mid(d->m_cursor));
2003 case Qt::ImTextBeforeCursor:
2004 if (argument.isValid())
2005 return QVariant(d->m_text.leftRef(d->m_cursor).right(argument.toInt()).toString());
2006 return QVariant(d->m_text.left(d->m_cursor));
2007 default:
2008 return QQuickItem::inputMethodQuery(property);
2009 }
2010}
2011#endif // im
2012
2013/*!
2014 \qmlmethod QtQuick::TextInput::deselect()
2015
2016 Removes active text selection.
2017*/
2018void QQuickTextInput::deselect()
2019{
2020 Q_D(QQuickTextInput);
2021 d->deselect();
2022}
2023
2024/*!
2025 \qmlmethod QtQuick::TextInput::selectAll()
2026
2027 Causes all text to be selected.
2028*/
2029void QQuickTextInput::selectAll()
2030{
2031 Q_D(QQuickTextInput);
2032 d->setSelection(0, text().length());
2033}
2034
2035/*!
2036 \qmlmethod QtQuick::TextInput::isRightToLeft(int start, int end)
2037
2038 Returns true if the natural reading direction of the editor text
2039 found between positions \a start and \a end is right to left.
2040*/
2041bool QQuickTextInput::isRightToLeft(int start, int end)
2042{
2043 if (start > end) {
2044 qmlWarning(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
2045 return false;
2046 } else {
2047 return text().midRef(start, end - start).isRightToLeft();
2048 }
2049}
2050
2051#if QT_CONFIG(clipboard)
2052/*!
2053 \qmlmethod QtQuick::TextInput::cut()
2054
2055 Moves the currently selected text to the system clipboard.
2056
2057 \note If the echo mode is set to a mode other than Normal then cut
2058 will not work. This is to prevent using cut as a method of bypassing
2059 password features of the line control.
2060*/
2061void QQuickTextInput::cut()
2062{
2063 Q_D(QQuickTextInput);
2064 if (!d->m_readOnly && d->m_echoMode == QQuickTextInput::Normal) {
2065 d->copy();
2066 d->del();
2067 }
2068}
2069
2070/*!
2071 \qmlmethod QtQuick::TextInput::copy()
2072
2073 Copies the currently selected text to the system clipboard.
2074
2075 \note If the echo mode is set to a mode other than Normal then copy
2076 will not work. This is to prevent using copy as a method of bypassing
2077 password features of the line control.
2078*/
2079void QQuickTextInput::copy()
2080{
2081 Q_D(QQuickTextInput);
2082 d->copy();
2083}
2084
2085/*!
2086 \qmlmethod QtQuick::TextInput::paste()
2087
2088 Replaces the currently selected text by the contents of the system clipboard.
2089*/
2090void QQuickTextInput::paste()
2091{
2092 Q_D(QQuickTextInput);
2093 if (!d->m_readOnly)
2094 d->paste();
2095}
2096#endif // clipboard
2097
2098/*!
2099 \qmlmethod QtQuick::TextInput::undo()
2100
2101 Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
2102 current selection, and updates the selection start to the current cursor
2103 position.
2104*/
2105
2106void QQuickTextInput::undo()
2107{
2108 Q_D(QQuickTextInput);
2109 if (!d->m_readOnly) {
2110 d->resetInputMethod();
2111 d->internalUndo();
2112 d->finishChange(-1, true);
2113 }
2114}
2115
2116/*!
2117 \qmlmethod QtQuick::TextInput::redo()
2118
2119 Redoes the last operation if redo is \l {canRedo}{available}.
2120*/
2121
2122void QQuickTextInput::redo()
2123{
2124 Q_D(QQuickTextInput);
2125 if (!d->m_readOnly) {
2126 d->resetInputMethod();
2127 d->internalRedo();
2128 d->finishChange();
2129 }
2130}
2131
2132/*!
2133 \qmlmethod QtQuick::TextInput::insert(int position, string text)
2134
2135 Inserts \a text into the TextInput at position.
2136*/
2137
2138void QQuickTextInput::insert(int position, const QString &text)
2139{
2140 Q_D(QQuickTextInput);
2141 if (d->m_echoMode == QQuickTextInput::Password) {
2142 if (d->m_passwordMaskDelay > 0)
2143 d->m_passwordEchoTimer.start(d->m_passwordMaskDelay, this);
2144 }
2145 if (position < 0 || position > d->m_text.length())
2146 return;
2147
2148 const int priorState = d->m_undoState;
2149
2150 QString insertText = text;
2151
2152 if (d->hasSelectedText()) {
2153 d->addCommand(QQuickTextInputPrivate::Command(
2154 QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
2155 }
2156 if (d->m_maskData) {
2157 insertText = d->maskString(position, insertText);
2158 for (int i = 0; i < insertText.length(); ++i) {
2159 d->addCommand(QQuickTextInputPrivate::Command(
2160 QQuickTextInputPrivate::DeleteSelection, position + i, d->m_text.at(position + i), -1, -1));
2161 d->addCommand(QQuickTextInputPrivate::Command(
2162 QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
2163 }
2164 d->m_text.replace(position, insertText.length(), insertText);
2165 if (!insertText.isEmpty())
2166 d->m_textDirty = true;
2167 if (position < d->m_selend && position + insertText.length() > d->m_selstart)
2168 d->m_selDirty = true;
2169 } else {
2170 int remaining = d->m_maxLength - d->m_text.length();
2171 if (remaining != 0) {
2172 insertText = insertText.left(remaining);
2173 d->m_text.insert(position, insertText);
2174 for (int i = 0; i < insertText.length(); ++i)
2175 d->addCommand(QQuickTextInputPrivate::Command(
2176 QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
2177 if (d->m_cursor >= position)
2178 d->m_cursor += insertText.length();
2179 if (d->m_selstart >= position)
2180 d->m_selstart += insertText.length();
2181 if (d->m_selend >= position)
2182 d->m_selend += insertText.length();
2183 d->m_textDirty = true;
2184 if (position >= d->m_selstart && position <= d->m_selend)
2185 d->m_selDirty = true;
2186 }
2187 }
2188
2189 d->addCommand(QQuickTextInputPrivate::Command(
2190 QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
2191 d->finishChange(priorState);
2192
2193 if (d->lastSelectionStart != d->lastSelectionEnd) {
2194 if (d->m_selstart != d->lastSelectionStart) {
2195 d->lastSelectionStart = d->m_selstart;
2196 emit selectionStartChanged();
2197 }
2198 if (d->m_selend != d->lastSelectionEnd) {
2199 d->lastSelectionEnd = d->m_selend;
2200 emit selectionEndChanged();
2201 }
2202 }
2203}
2204
2205/*!
2206 \qmlmethod QtQuick::TextInput::remove(int start, int end)
2207
2208 Removes the section of text that is between the \a start and \a end positions from the TextInput.
2209*/
2210
2211void QQuickTextInput::remove(int start, int end)
2212{
2213 Q_D(QQuickTextInput);
2214
2215 start = qBound(0, start, d->m_text.length());
2216 end = qBound(0, end, d->m_text.length());
2217
2218 if (start > end)
2219 qSwap(start, end);
2220 else if (start == end)
2221 return;
2222
2223 if (start < d->m_selend && end > d->m_selstart)
2224 d->m_selDirty = true;
2225
2226 const int priorState = d->m_undoState;
2227
2228 d->addCommand(QQuickTextInputPrivate::Command(
2229 QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
2230
2231 if (start <= d->m_cursor && d->m_cursor < end) {
2232 // cursor is within the selection. Split up the commands
2233 // to be able to restore the correct cursor position
2234 for (int i = d->m_cursor; i >= start; --i) {
2235 d->addCommand(QQuickTextInputPrivate::Command(
2236 QQuickTextInputPrivate::DeleteSelection, i, d->m_text.at(i), -1, 1));
2237 }
2238 for (int i = end - 1; i > d->m_cursor; --i) {
2239 d->addCommand(QQuickTextInputPrivate::Command(
2240 QQuickTextInputPrivate::DeleteSelection, i - d->m_cursor + start - 1, d->m_text.at(i), -1, -1));
2241 }
2242 } else {
2243 for (int i = end - 1; i >= start; --i) {
2244 d->addCommand(QQuickTextInputPrivate::Command(
2245 QQuickTextInputPrivate::RemoveSelection, i, d->m_text.at(i), -1, -1));
2246 }
2247 }
2248 if (d->m_maskData) {
2249 d->m_text.replace(start, end - start, d->clearString(start, end - start));
2250 for (int i = 0; i < end - start; ++i) {
2251 d->addCommand(QQuickTextInputPrivate::Command(
2252 QQuickTextInputPrivate::Insert, start + i, d->m_text.at(start + i), -1, -1));
2253 }
2254 } else {
2255 d->m_text.remove(start, end - start);
2256
2257 if (d->m_cursor > start)
2258 d->m_cursor -= qMin(d->m_cursor, end) - start;
2259 if (d->m_selstart > start)
2260 d->m_selstart -= qMin(d->m_selstart, end) - start;
2261 if (d->m_selend > end)
2262 d->m_selend -= qMin(d->m_selend, end) - start;
2263 }
2264 d->addCommand(QQuickTextInputPrivate::Command(
2265 QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
2266
2267 d->m_textDirty = true;
2268 d->finishChange(priorState);
2269
2270 if (d->lastSelectionStart != d->lastSelectionEnd) {
2271 if (d->m_selstart != d->lastSelectionStart) {
2272 d->lastSelectionStart = d->m_selstart;
2273 emit selectionStartChanged();
2274 }
2275 if (d->m_selend != d->lastSelectionEnd) {
2276 d->lastSelectionEnd = d->m_selend;
2277 emit selectionEndChanged();
2278 }
2279 }
2280}
2281
2282
2283/*!
2284 \qmlmethod QtQuick::TextInput::selectWord()
2285
2286 Causes the word closest to the current cursor position to be selected.
2287*/
2288void QQuickTextInput::selectWord()
2289{
2290 Q_D(QQuickTextInput);
2291 d->selectWordAtPos(d->m_cursor);
2292}
2293
2294/*!
2295 \qmlproperty string QtQuick::TextInput::passwordCharacter
2296
2297 This is the character displayed when echoMode is set to Password or
2298 PasswordEchoOnEdit. By default it is the password character used by
2299 the platform theme.
2300
2301 If this property is set to a string with more than one character,
2302 the first character is used. If the string is empty, the value
2303 is ignored and the property is not set.
2304*/
2305QString QQuickTextInput::passwordCharacter() const
2306{
2307 Q_D(const QQuickTextInput);
2308 return QString(d->m_passwordCharacter);
2309}
2310
2311void QQuickTextInput::setPasswordCharacter(const QString &str)
2312{
2313 Q_D(QQuickTextInput);
2314 if (str.length() < 1)
2315 return;
2316 d->m_passwordCharacter = str.constData()[0];
2317 if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit)
2318 d->updateDisplayText();
2319 emit passwordCharacterChanged();
2320}
2321
2322/*!
2323 \qmlproperty int QtQuick::TextInput::passwordMaskDelay
2324 \since 5.4
2325
2326 Sets the delay before visible character is masked with password character, in milliseconds.
2327
2328 The reset method will be called by assigning undefined.
2329*/
2330int QQuickTextInput::passwordMaskDelay() const
2331{
2332 Q_D(const QQuickTextInput);
2333 return d->m_passwordMaskDelay;
2334}
2335
2336void QQuickTextInput::setPasswordMaskDelay(int delay)
2337{
2338 Q_D(QQuickTextInput);
2339 if (d->m_passwordMaskDelay != delay) {
2340 d->m_passwordMaskDelay = delay;
2341 emit passwordMaskDelayChanged(delay);
2342 }
2343}
2344
2345void QQuickTextInput::resetPasswordMaskDelay()
2346{
2347 setPasswordMaskDelay(qGuiApp->styleHints()->passwordMaskDelay());
2348}
2349
2350/*!
2351 \qmlproperty string QtQuick::TextInput::displayText
2352
2353 This is the text displayed in the TextInput.
2354
2355 If \l echoMode is set to TextInput::Normal, this holds the
2356 same value as the TextInput::text property. Otherwise,
2357 this property holds the text visible to the user, while
2358 the \l text property holds the actual entered text.
2359
2360 \note Unlike the TextInput::text property, this contains
2361 partial text input from an input method.
2362
2363 \readonly
2364 \sa preeditText
2365*/
2366QString QQuickTextInput::displayText() const
2367{
2368 Q_D(const QQuickTextInput);
2369 return d->m_textLayout.text().insert(d->m_textLayout.preeditAreaPosition(), d->m_textLayout.preeditAreaText());
2370}
2371
2372/*!
2373 \qmlproperty string QtQuick::TextInput::preeditText
2374 \readonly
2375 \since 5.7
2376
2377 This property contains partial text input from an input method.
2378
2379 \sa displayText
2380*/
2381QString QQuickTextInput::preeditText() const
2382{
2383 Q_D(const QQuickTextInput);
2384 return d->m_textLayout.preeditAreaText();
2385}
2386
2387/*!
2388 \qmlproperty bool QtQuick::TextInput::selectByMouse
2389
2390 Defaults to false.
2391
2392 If true, the user can use the mouse to select text in some
2393 platform-specific way. Note that for some platforms this may
2394 not be an appropriate interaction (it may conflict with how
2395 the text needs to behave inside a \l Flickable, for example).
2396*/
2397bool QQuickTextInput::selectByMouse() const
2398{
2399 Q_D(const QQuickTextInput);
2400 return d->selectByMouse;
2401}
2402
2403void QQuickTextInput::setSelectByMouse(bool on)
2404{
2405 Q_D(QQuickTextInput);
2406 if (d->selectByMouse != on) {
2407 d->selectByMouse = on;
2408 emit selectByMouseChanged(on);
2409 }
2410}
2411
2412/*!
2413 \qmlproperty enumeration QtQuick::TextInput::mouseSelectionMode
2414
2415 Specifies how text should be selected using a mouse.
2416
2417 \list
2418 \li TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
2419 \li TextInput.SelectWords - The selection is updated with whole words.
2420 \endlist
2421
2422 This property only applies when \l selectByMouse is true.
2423*/
2424
2425QQuickTextInput::SelectionMode QQuickTextInput::mouseSelectionMode() const
2426{
2427 Q_D(const QQuickTextInput);
2428 return d->mouseSelectionMode;
2429}
2430
2431void QQuickTextInput::setMouseSelectionMode(SelectionMode mode)
2432{
2433 Q_D(QQuickTextInput);
2434 if (d->mouseSelectionMode != mode) {
2435 d->mouseSelectionMode = mode;
2436 emit mouseSelectionModeChanged(mode);
2437 }
2438}
2439
2440/*!
2441 \qmlproperty bool QtQuick::TextInput::persistentSelection
2442
2443 Whether the TextInput should keep its selection when it loses active focus to another
2444 item in the scene. By default this is set to false;
2445*/
2446
2447bool QQuickTextInput::persistentSelection() const
2448{
2449 Q_D(const QQuickTextInput);
2450 return d->persistentSelection;
2451}
2452
2453void QQuickTextInput::setPersistentSelection(bool on)
2454{
2455 Q_D(QQuickTextInput);
2456 if (d->persistentSelection == on)
2457 return;
2458 d->persistentSelection = on;
2459 emit persistentSelectionChanged();
2460}
2461
2462/*!
2463 \qmlproperty bool QtQuick::TextInput::canPaste
2464
2465 Returns true if the TextInput is writable and the content of the clipboard is
2466 suitable for pasting into the TextInput.
2467*/
2468bool QQuickTextInput::canPaste() const
2469{
2470#if QT_CONFIG(clipboard)
2471 Q_D(const QQuickTextInput);
2472 if (!d->canPasteValid) {
2473 if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
2474 const_cast<QQuickTextInputPrivate *>(d)->canPaste = !d->m_readOnly && mimeData->hasText();
2475 const_cast<QQuickTextInputPrivate *>(d)->canPasteValid = true;
2476 }
2477 return d->canPaste;
2478#else
2479 return false;
2480#endif
2481}
2482
2483/*!
2484 \qmlproperty bool QtQuick::TextInput::canUndo
2485
2486 Returns true if the TextInput is writable and there are previous operations
2487 that can be undone.
2488*/
2489
2490bool QQuickTextInput::canUndo() const
2491{
2492 Q_D(const QQuickTextInput);
2493 return d->canUndo;
2494}
2495
2496/*!
2497 \qmlproperty bool QtQuick::TextInput::canRedo
2498
2499 Returns true if the TextInput is writable and there are \l {undo}{undone}
2500 operations that can be redone.
2501*/
2502
2503bool QQuickTextInput::canRedo() const
2504{
2505 Q_D(const QQuickTextInput);
2506 return d->canRedo;
2507}
2508
2509/*!
2510 \qmlproperty real QtQuick::TextInput::contentWidth
2511
2512 Returns the width of the text, including the width past the width
2513 which is covered due to insufficient wrapping if \l wrapMode is set.
2514*/
2515
2516qreal QQuickTextInput::contentWidth() const
2517{
2518 Q_D(const QQuickTextInput);
2519 return d->contentSize.width();
2520}
2521
2522/*!
2523 \qmlproperty real QtQuick::TextInput::contentHeight
2524
2525 Returns the height of the text, including the height past the height
2526 that is covered if the text does not fit within the set height.
2527*/
2528
2529qreal QQuickTextInput::contentHeight() const
2530{
2531 Q_D(const QQuickTextInput);
2532 return d->contentSize.height();
2533}
2534
2535void QQuickTextInput::moveCursorSelection(int position)
2536{
2537 Q_D(QQuickTextInput);
2538 d->moveCursor(position, true);
2539}
2540
2541/*!
2542 \qmlmethod QtQuick::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters)
2543
2544 Moves the cursor to \a position and updates the selection according to the optional \a mode
2545 parameter. (To only move the cursor, set the \l cursorPosition property.)
2546
2547 When this method is called it additionally sets either the
2548 selectionStart or the selectionEnd (whichever was at the previous cursor position)
2549 to the specified position. This allows you to easily extend and contract the selected
2550 text range.
2551
2552 The selection mode specifies whether the selection is updated on a per character or a per word
2553 basis. If not specified the selection mode will default to TextInput.SelectCharacters.
2554
2555 \list
2556 \li TextInput.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
2557 the previous cursor position) to the specified position.
2558 \li TextInput.SelectWords - Sets the selectionStart and selectionEnd to include all
2559 words between the specified position and the previous cursor position. Words partially in the
2560 range are included.
2561 \endlist
2562
2563 For example, take this sequence of calls:
2564
2565 \code
2566 cursorPosition = 5
2567 moveCursorSelection(9, TextInput.SelectCharacters)
2568 moveCursorSelection(7, TextInput.SelectCharacters)
2569 \endcode
2570
2571 This moves the cursor to position 5, extend the selection end from 5 to 9
2572 and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
2573 selected (the 6th and 7th characters).
2574
2575 The same sequence with TextInput.SelectWords will extend the selection start to a word boundary
2576 before or on position 5 and extend the selection end to a word boundary on or past position 9.
2577*/
2578void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
2579{
2580 Q_D(QQuickTextInput);
2581
2582 if (mode == SelectCharacters) {
2583 d->moveCursor(pos, true);
2584 } else if (pos != d->m_cursor){
2585 const int cursor = d->m_cursor;
2586 int anchor;
2587 if (!d->hasSelectedText())
2588 anchor = d->m_cursor;
2589 else if (d->selectionStart() == d->m_cursor)
2590 anchor = d->selectionEnd();
2591 else
2592 anchor = d->selectionStart();
2593
2594 if (anchor < pos || (anchor == pos && cursor < pos)) {
2595 const QString text = this->text();
2596 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
2597 finder.setPosition(anchor);
2598
2599 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
2600 if (anchor < text.length() && (reasons == QTextBoundaryFinder::NotAtBoundary
2601 || (reasons & QTextBoundaryFinder::EndOfItem))) {
2602 finder.toPreviousBoundary();
2603 }
2604 anchor = finder.position() != -1 ? finder.position() : 0;
2605
2606 finder.setPosition(pos);
2607 if (pos > 0 && !finder.boundaryReasons())
2608 finder.toNextBoundary();
2609 const int cursor = finder.position() != -1 ? finder.position() : text.length();
2610
2611 d->setSelection(anchor, cursor - anchor);
2612 } else if (anchor > pos || (anchor == pos && cursor > pos)) {
2613 const QString text = this->text();
2614 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
2615 finder.setPosition(anchor);
2616
2617 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
2618 if (anchor > 0 && (reasons == QTextBoundaryFinder::NotAtBoundary
2619 || (reasons & QTextBoundaryFinder::StartOfItem))) {
2620 finder.toNextBoundary();
2621 }
2622 anchor = finder.position() != -1 ? finder.position() : text.length();
2623
2624 finder.setPosition(pos);
2625 if (pos < text.length() && !finder.boundaryReasons())
2626 finder.toPreviousBoundary();
2627 const int cursor = finder.position() != -1 ? finder.position() : 0;
2628
2629 d->setSelection(anchor, cursor - anchor);
2630 }
2631 }
2632}
2633
2634void QQuickTextInput::focusInEvent(QFocusEvent *event)
2635{
2636 Q_D(QQuickTextInput);
2637 d->handleFocusEvent(event);
2638 QQuickImplicitSizeItem::focusInEvent(event);
2639}
2640
2641void QQuickTextInputPrivate::handleFocusEvent(QFocusEvent *event)
2642{
2643 Q_Q(QQuickTextInput);
2644 bool focus = event->gotFocus();
2645 if (!m_readOnly) {
2646 q->setCursorVisible(focus);
2647 setBlinkingCursorEnabled(focus);
2648 }
2649 if (focus) {
2650 q->q_updateAlignment();
2651#if QT_CONFIG(im)
2652 if (focusOnPress && !m_readOnly)
2653 qGuiApp->inputMethod()->show();
2654 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
2655 q, SLOT(q_updateAlignment()));
2656#endif
2657 } else {
2658 if ((m_passwordEchoEditing || m_passwordEchoTimer.isActive())) {
2659 updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
2660 }
2661
2662 if (event->reason() != Qt::ActiveWindowFocusReason
2663 && event->reason() != Qt::PopupFocusReason
2664 && hasSelectedText()
2665 && !persistentSelection)
2666 deselect();
2667
2668 if (hasAcceptableInput(m_text) == AcceptableInput || fixup())
2669 emit q->editingFinished();
2670
2671#if QT_CONFIG(im)
2672 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
2673 q, SLOT(q_updateAlignment()));
2674#endif
2675 }
2676}
2677
2678void QQuickTextInput::focusOutEvent(QFocusEvent *event)
2679{
2680 Q_D(QQuickTextInput);
2681 d->handleFocusEvent(event);
2682 QQuickImplicitSizeItem::focusOutEvent(event);
2683}
2684
2685/*!
2686 \qmlproperty bool QtQuick::TextInput::inputMethodComposing
2687
2688
2689 This property holds whether the TextInput has partial text input from an
2690 input method.
2691
2692 While it is composing an input method may rely on mouse or key events from
2693 the TextInput to edit or commit the partial text. This property can be
2694 used to determine when to disable events handlers that may interfere with
2695 the correct operation of an input method.
2696*/
2697bool QQuickTextInput::isInputMethodComposing() const
2698{
2699#if !QT_CONFIG(im)
2700 return false;
2701#else
2702 Q_D(const QQuickTextInput);
2703 return d->hasImState;
2704#endif
2705}
2706
2707QQuickTextInputPrivate::ExtraData::ExtraData()
2708 : padding(0)
2709 , topPadding(0)
2710 , leftPadding(0)
2711 , rightPadding(0)
2712 , bottomPadding(0)
2713 , explicitTopPadding(false)
2714 , explicitLeftPadding(false)
2715 , explicitRightPadding(false)
2716 , explicitBottomPadding(false)
2717 , implicitResize(true)
2718{
2719}
2720
2721void QQuickTextInputPrivate::init()
2722{
2723 Q_Q(QQuickTextInput);
2724#if QT_CONFIG(clipboard)
2725 if (QGuiApplication::clipboard()->supportsSelection())
2726 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2727 else
2728#endif
2729 q->setAcceptedMouseButtons(Qt::LeftButton);
2730
2731#if QT_CONFIG(im)
2732 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2733#endif
2734 q->setFlag(QQuickItem::ItemHasContents);
2735#if QT_CONFIG(clipboard)
2736 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()),
2737 q, QQuickTextInput, SLOT(q_canPasteChanged()));
2738#endif // clipboard
2739
2740 lastSelectionStart = 0;
2741 lastSelectionEnd = 0;
2742 determineHorizontalAlignment();
2743
2744 if (!qmlDisableDistanceField()) {
2745 QTextOption option = m_textLayout.textOption();
2746 option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
2747 m_textLayout.setTextOption(option);
2748 }
2749
2750 m_inputControl = new QInputControl(QInputControl::LineEdit, q);
2751}
2752
2753void QQuickTextInputPrivate::resetInputMethod()
2754{
2755 Q_Q(QQuickTextInput);
2756 if (!m_readOnly && q->hasActiveFocus() && qGuiApp)
2757 QGuiApplication::inputMethod()->reset();
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(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(d->contentSize.width(), width(), 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("");
2874 else
2875 str = m_text;
2876
2877 if (m_echoMode == QQuickTextInput::Password) {
2878 str.fill(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(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(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(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].unicode() < 0x20 && uc[i] != QChar::Tabulation)
2901 || uc[i] == QChar::LineSeparator
2902 || uc[i] == QChar::ParagraphSeparator
2903 || uc[i] == QChar::ObjectReplacementCharacter)
2904 uc[i] = QChar(0x0020);
2905 }
2906
2907 if (str != orig || forceUpdate) {
2908 m_textLayout.setText(str);
2909 updateLayout(); // polish?
2910 emit q_func()->displayTextChanged();
2911 }
2912}
2913
2914qreal QQuickTextInputPrivate::getImplicitWidth() const
2915{
2916 Q_Q(const QQuickTextInput);
2917 if (!requireImplicitWidth) {
2918 QQuickTextInputPrivate *d = const_cast<QQuickTextInputPrivate *>(this);
2919 d->requireImplicitWidth = true;
2920
2921 if (q->isComponentComplete()) {
2922 // One time cost, only incurred if implicitWidth is first requested after
2923 // componentComplete.
2924 QTextLayout layout(m_text);
2925
2926 QTextOption option = m_textLayout.textOption();
2927 option.setTextDirection(m_layoutDirection);
2928 option.setFlags(QTextOption::IncludeTrailingSpaces);
2929 option.setWrapMode(QTextOption::WrapMode(wrapMode));
2930 option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
2931 layout.setTextOption(option);
2932 layout.setFont(font);
2933#if QT_CONFIG(im)
2934 layout.setPreeditArea(m_textLayout.preeditAreaPosition(), m_textLayout.preeditAreaText());
2935#endif
2936 layout.beginLayout();
2937
2938 QTextLine line = layout.createLine();
2939 line.setLineWidth(INT_MAX);
2940 d->implicitWidth = qCeil(line.naturalTextWidth()) + q->leftPadding() + q->rightPadding();
2941
2942 layout.endLayout();
2943 }
2944 }
2945 return implicitWidth;
2946}
2947
2948void QQuickTextInputPrivate::setTopPadding(qreal value, bool reset)
2949{
2950 Q_Q(QQuickTextInput);
2951 qreal oldPadding = q->topPadding();
2952 if (!reset || extra.isAllocated()) {
2953 extra.value().topPadding = value;
2954 extra.value().explicitTopPadding = !reset;
2955 }
2956 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
2957 updateLayout();
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(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
2971 updateLayout();
2972 emit q->leftPaddingChanged();
2973 }
2974}
2975
2976void QQuickTextInputPrivate::setRightPadding(qreal value, bool reset)
2977{
2978 Q_Q(QQuickTextInput);
2979 qreal oldPadding = q->rightPadding();
2980 if (!reset || extra.isAllocated()) {
2981 extra.value().rightPadding = value;
2982 extra.value().explicitRightPadding = !reset;
2983 }
2984 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
2985 updateLayout();
2986 emit q->rightPaddingChanged();
2987 }
2988}
2989
2990void QQuickTextInputPrivate::setBottomPadding(qreal value, bool reset)
2991{
2992 Q_Q(QQuickTextInput);
2993 qreal oldPadding = q->bottomPadding();
2994 if (!reset || extra.isAllocated()) {
2995 extra.value().bottomPadding = value;
2996 extra.value().explicitBottomPadding = !reset;
2997 }
2998 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
2999 updateLayout();
3000 emit q->bottomPaddingChanged();
3001 }
3002}
3003
3004bool QQuickTextInputPrivate::isImplicitResizeEnabled() const
3005{
3006 return !extra.isAllocated() || extra->implicitResize;
3007}
3008
3009void QQuickTextInputPrivate::setImplicitResizeEnabled(bool enabled)
3010{
3011 if (!enabled)
3012 extra.value().implicitResize = false;
3013 else if (extra.isAllocated())
3014 extra->implicitResize = true;
3015}
3016
3017void QQuickTextInputPrivate::updateLayout()
3018{
3019 Q_Q(QQuickTextInput);
3020
3021 if (!q->isComponentComplete())
3022 return;
3023
3024
3025 QTextOption option = m_textLayout.textOption();
3026 option.setTextDirection(layoutDirection());
3027 option.setWrapMode(QTextOption::WrapMode(wrapMode));
3028 option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
3029 if (!qmlDisableDistanceField())
3030 option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
3031
3032 m_textLayout.setTextOption(option);
3033 m_textLayout.setFont(font);
3034
3035 m_textLayout.beginLayout();
3036
3037 QTextLine line = m_textLayout.createLine();
3038 if (requireImplicitWidth) {
3039 line.setLineWidth(INT_MAX);
3040 const bool wasInLayout = inLayout;
3041 inLayout = true;
3042 if (isImplicitResizeEnabled())
3043 q->setImplicitWidth(qCeil(line.naturalTextWidth()) + q->leftPadding() + q->rightPadding());
3044 inLayout = wasInLayout;
3045 if (inLayout) // probably the result of a binding loop, but by letting it
3046 return; // get this far we'll get a warning to that effect.
3047 }
3048 qreal lineWidth = q->widthValid() || !isImplicitResizeEnabled() ? q->width() - q->leftPadding() - q->rightPadding() : INT_MAX;
3049 qreal height = 0;
3050 qreal width = 0;
3051 do {
3052 line.setLineWidth(lineWidth);
3053 line.setPosition(QPointF(0, height));
3054
3055 height += line.height();
3056 width = qMax(width, line.naturalTextWidth());
3057
3058 line = m_textLayout.createLine();
3059 } while (line.isValid());
3060 m_textLayout.endLayout();
3061
3062 option.setWrapMode(QTextOption::NoWrap);
3063 m_textLayout.setTextOption(option);
3064
3065 textLayoutDirty = true;
3066
3067 const QSizeF previousSize = contentSize;
3068 contentSize = QSizeF(width, height);
3069
3070 updateType = UpdatePaintNode;
3071 q->polish();
3072 q->update();
3073
3074 if (isImplicitResizeEnabled()) {
3075 if (!requireImplicitWidth && !q->widthValid())
3076 q->setImplicitSize(width + q->leftPadding() + q->rightPadding(), height + q->topPadding() + q->bottomPadding());
3077 else
3078 q->setImplicitHeight(height + q->topPadding() + q->bottomPadding());
3079 }
3080
3081 updateBaselineOffset();
3082
3083 if (previousSize != contentSize)
3084 emit q->contentSizeChanged();
3085}
3086
3087/*!
3088 \internal
3089 \brief QQuickTextInputPrivate::updateBaselineOffset
3090
3091 Assumes contentSize.height() is already calculated.
3092 */
3093void QQuickTextInputPrivate::updateBaselineOffset()
3094{
3095 Q_Q(QQuickTextInput);
3096 if (!q->isComponentComplete())
3097 return;
3098 QFontMetricsF fm(font);
3099 qreal yoff = 0;
3100 if (q->heightValid()) {
3101 const qreal surplusHeight = q->height() - contentSize.height() - q->topPadding() - q->bottomPadding();
3102 if (vAlign == QQuickTextInput::AlignBottom)
3103 yoff = surplusHeight;
3104 else if (vAlign == QQuickTextInput::AlignVCenter)
3105 yoff = surplusHeight/2;
3106 }
3107 q->setBaselineOffset(fm.ascent() + yoff + q->topPadding());
3108}
3109
3110#if QT_CONFIG(clipboard)
3111/*!
3112 \internal
3113
3114 Copies the currently selected text into the clipboard using the given
3115 \a mode.
3116
3117 \note If the echo mode is set to a mode other than Normal then copy
3118 will not work. This is to prevent using copy as a method of bypassing
3119 password features of the line control.
3120*/
3121void QQuickTextInputPrivate::copy(QClipboard::Mode mode) const
3122{
3123 QString t = selectedText();
3124 if (!t.isEmpty() && m_echoMode == QQuickTextInput::Normal) {
3125 QGuiApplication::clipboard()->setText(t, mode);
3126 }
3127}
3128
3129/*!
3130 \internal
3131
3132 Inserts the text stored in the application clipboard into the line
3133 control.
3134
3135 \sa insert()
3136*/
3137void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode)
3138{
3139 QString clip = QGuiApplication::clipboard()->text(clipboardMode);
3140 if (!clip.isEmpty() || hasSelectedText()) {
3141 separate(); //make it a separate undo/redo command
3142 insert(clip);
3143 separate();
3144 }
3145}
3146
3147#endif // clipboard
3148
3149#if QT_CONFIG(im)
3150/*!
3151 \internal
3152*/
3153void QQuickTextInputPrivate::commitPreedit()
3154{
3155 Q_Q(QQuickTextInput);
3156
3157 if (!hasImState)
3158 return;
3159
3160 QGuiApplication::inputMethod()->commit();
3161
3162 if (!hasImState)
3163 return;
3164
3165 QInputMethodEvent ev;
3166 QCoreApplication::sendEvent(q, &ev);
3167}
3168
3169void QQuickTextInputPrivate::cancelPreedit()
3170{
3171 Q_Q(QQuickTextInput);
3172
3173 if (!hasImState)
3174 return;
3175
3176 QGuiApplication::inputMethod()->reset();
3177
3178 QInputMethodEvent ev;
3179 QCoreApplication::sendEvent(q, &ev);
3180}
3181#endif // im
3182
3183/*!
3184 \internal
3185
3186 Handles the behavior for the backspace key or function.
3187 Removes the current selection if there is a selection, otherwise
3188 removes the character prior to the cursor position.
3189
3190 \sa del()
3191*/
3192void QQuickTextInputPrivate::backspace()
3193{
3194 int priorState = m_undoState;
3195 if (separateSelection()) {
3196 removeSelectedText();
3197 } else if (m_cursor) {
3198 --m_cursor;
3199 if (m_maskData)
3200 m_cursor = prevMaskBlank(m_cursor);
3201 QChar uc = m_text.at(m_cursor);
3202 if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
3203 // second half of a surrogate, check if we have the first half as well,
3204 // if yes delete both at once
3205 uc = m_text.at(m_cursor - 1);
3206 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
3207 internalDelete(true);
3208 --m_cursor;
3209 }
3210 }
3211 internalDelete(true);
3212 }
3213 finishChange(priorState);
3214}
3215
3216/*!
3217 \internal
3218
3219 Handles the behavior for the delete key or function.
3220 Removes the current selection if there is a selection, otherwise
3221 removes the character after the cursor position.
3222
3223 \sa del()
3224*/
3225void QQuickTextInputPrivate::del()
3226{
3227 int priorState = m_undoState;
3228 if (separateSelection()) {
3229 removeSelectedText();
3230 } else {
3231 int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
3232 while (n--)
3233 internalDelete();
3234 }
3235 finishChange(priorState);
3236}
3237
3238/*!
3239 \internal
3240
3241 Inserts the given \a newText at the current cursor position.
3242 If there is any selected text it is removed prior to insertion of
3243 the new text.
3244*/
3245void QQuickTextInputPrivate::insert(const QString &newText)
3246{
3247 int priorState = m_undoState;
3248 if (separateSelection())
3249 removeSelectedText();
3250 internalInsert(newText);
3251 finishChange(priorState);
3252}
3253
3254/*!
3255 \internal
3256
3257 Clears the line control text.
3258*/
3259void QQuickTextInputPrivate::clear()
3260{
3261 int priorState = m_undoState;
3262 separateSelection();
3263 m_selstart = 0;
3264 m_selend = m_text.length();
3265 removeSelectedText();
3266 separate();
3267 finishChange(priorState, /*update*/false, /*edited*/false);
3268}
3269
3270/*!
3271 \internal
3272
3273 Sets \a length characters from the given \a start position as selected.
3274 The given \a start position must be within the current text for
3275 the line control. If \a length characters cannot be selected, then
3276 the selection will extend to the end of the current text.
3277*/
3278void QQuickTextInputPrivate::setSelection(int start, int length)
3279{
3280 Q_Q(QQuickTextInput);
3281#if QT_CONFIG(im)
3282 commitPreedit();
3283#endif
3284
3285 if (start < 0 || start > m_text.length()) {
3286 qWarning("QQuickTextInputPrivate::setSelection: Invalid start position");
3287 return;
3288 }
3289
3290 if (length > 0) {
3291 if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
3292 return;
3293 m_selstart = start;
3294 m_selend = qMin(start + length, m_text.length());
3295 m_cursor = m_selend;
3296 } else if (length < 0){
3297 if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
3298 return;
3299 m_selstart = qMax(start + length, 0);
3300 m_selend = start;
3301 m_cursor = m_selstart;
3302 } else if (m_selstart != m_selend) {
3303 m_selstart = 0;
3304 m_selend = 0;
3305 m_cursor = start;
3306 } else {
3307 m_cursor = start;
3308 emitCursorPositionChanged();
3309 return;
3310 }
3311 emit q->selectionChanged();
3312 emitCursorPositionChanged();
3313#if QT_CONFIG(im)
3314 q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImCursorPosition | Qt::