1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquicktextinput_p.h"
43 #include "qquicktextinput_p_p.h"
44 #include "qquickcanvas.h"
46 #include <private/qdeclarativeglobal_p.h>
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QTextBoundaryFinder>
51 #include "qquicktextnode_p.h"
52 #include <QtQuick/qsgsimplerectnode.h>
54 #include <QtGui/qstylehints.h>
55 #include <QtGui/qinputpanel.h>
57 #ifndef QT_NO_ACCESSIBILITY
58 #include "qaccessible.h"
63 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
66 \qmlclass TextInput QQuickTextInput
67 \inqmlmodule QtQuick 2
68 \ingroup qml-basic-visual-elements
69 \brief The TextInput item displays an editable line of text.
72 The TextInput element displays a single line of editable plain text.
74 TextInput is used to accept a line of text input. Input constraints
75 can be placed on a TextInput item (for example, through a \l validator or \l inputMask),
76 and setting \l echoMode to an appropriate value enables TextInput to be used for
77 a password input field.
79 On Mac OS X, the Up/Down key bindings for Home/End are explicitly disabled.
80 If you want such bindings (on any platform), you will need to construct them in QML.
82 \sa TextEdit, Text, {declarative/text/textselection}{Text Selection example}
84 QQuickTextInput::QQuickTextInput(QQuickItem* parent)
85 : QQuickImplicitSizeItem(*(new QQuickTextInputPrivate), parent)
91 QQuickTextInput::~QQuickTextInput()
96 \qmlproperty string QtQuick2::TextInput::text
98 The text in the TextInput.
100 QString QQuickTextInput::text() const
102 Q_D(const QQuickTextInput);
104 QString content = d->m_text;
105 if (!d->m_tentativeCommit.isEmpty())
106 content.insert(d->m_cursor, d->m_tentativeCommit);
107 QString res = d->m_maskData ? d->stripString(content) : content;
108 return (res.isNull() ? QString::fromLatin1("") : res);
111 void QQuickTextInput::setText(const QString &s)
113 Q_D(QQuickTextInput);
116 if (d->composeMode())
117 qApp->inputPanel()->reset();
118 d->m_tentativeCommit.clear();
119 d->internalSetText(s, -1, false);
122 QString QQuickTextInputPrivate::realText() const
124 QString res = m_maskData ? stripString(m_text) : m_text;
125 return (res.isNull() ? QString::fromLatin1("") : res);
129 \qmlproperty string QtQuick2::TextInput::font.family
131 Sets the family name of the font.
133 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
134 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
135 If the family isn't available a family will be set using the font matching algorithm.
139 \qmlproperty bool QtQuick2::TextInput::font.bold
141 Sets whether the font weight is bold.
145 \qmlproperty enumeration QtQuick2::TextInput::font.weight
147 Sets the font's weight.
149 The weight can be one of:
152 \o Font.Normal - the default
159 TextInput { text: "Hello"; font.weight: Font.DemiBold }
164 \qmlproperty bool QtQuick2::TextInput::font.italic
166 Sets whether the font has an italic style.
170 \qmlproperty bool QtQuick2::TextInput::font.underline
172 Sets whether the text is underlined.
176 \qmlproperty bool QtQuick2::TextInput::font.strikeout
178 Sets whether the font has a strikeout style.
182 \qmlproperty real QtQuick2::TextInput::font.pointSize
184 Sets the font size in points. The point size must be greater than zero.
188 \qmlproperty int QtQuick2::TextInput::font.pixelSize
190 Sets the font size in pixels.
192 Using this function makes the font device dependent.
193 Use \c pointSize to set the size of the font in a device independent manner.
197 \qmlproperty real QtQuick2::TextInput::font.letterSpacing
199 Sets the letter spacing for the font.
201 Letter spacing changes the default spacing between individual letters in the font.
202 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
206 \qmlproperty real QtQuick2::TextInput::font.wordSpacing
208 Sets the word spacing for the font.
210 Word spacing changes the default spacing between individual words.
211 A positive value increases the word spacing by a corresponding amount of pixels,
212 while a negative value decreases the inter-word spacing accordingly.
216 \qmlproperty enumeration QtQuick2::TextInput::font.capitalization
218 Sets the capitalization for the text.
221 \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
222 \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
223 \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
224 \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
225 \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
229 TextInput { text: "Hello"; font.capitalization: Font.AllLowercase }
233 QFont QQuickTextInput::font() const
235 Q_D(const QQuickTextInput);
236 return d->sourceFont;
239 void QQuickTextInput::setFont(const QFont &font)
241 Q_D(QQuickTextInput);
242 if (d->sourceFont == font)
245 d->sourceFont = font;
246 QFont oldFont = d->font;
248 if (d->font.pointSizeF() != -1) {
250 qreal size = qRound(d->font.pointSizeF()*2.0);
251 d->font.setPointSizeF(size/2.0);
253 if (oldFont != d->font) {
254 d->updateDisplayText();
256 updateCursorRectangle();
258 d->cursorItem->setHeight(QFontMetrics(d->font).height());
261 emit fontChanged(d->sourceFont);
265 \qmlproperty color QtQuick2::TextInput::color
269 QColor QQuickTextInput::color() const
271 Q_D(const QQuickTextInput);
275 void QQuickTextInput::setColor(const QColor &c)
277 Q_D(QQuickTextInput);
280 d->textLayoutDirty = true;
282 emit colorChanged(c);
288 \qmlproperty color QtQuick2::TextInput::selectionColor
290 The text highlight color, used behind selections.
292 QColor QQuickTextInput::selectionColor() const
294 Q_D(const QQuickTextInput);
295 return d->selectionColor;
298 void QQuickTextInput::setSelectionColor(const QColor &color)
300 Q_D(QQuickTextInput);
301 if (d->selectionColor == color)
304 d->selectionColor = color;
305 d->m_palette.setColor(QPalette::Highlight, d->selectionColor);
306 if (d->hasSelectedText()) {
307 d->textLayoutDirty = true;
310 emit selectionColorChanged(color);
313 \qmlproperty color QtQuick2::TextInput::selectedTextColor
315 The highlighted text color, used in selections.
317 QColor QQuickTextInput::selectedTextColor() const
319 Q_D(const QQuickTextInput);
320 return d->selectedTextColor;
323 void QQuickTextInput::setSelectedTextColor(const QColor &color)
325 Q_D(QQuickTextInput);
326 if (d->selectedTextColor == color)
329 d->selectedTextColor = color;
330 d->m_palette.setColor(QPalette::HighlightedText, d->selectedTextColor);
331 if (d->hasSelectedText()) {
332 d->textLayoutDirty = true;
335 emit selectedTextColorChanged(color);
339 \qmlproperty enumeration QtQuick2::TextInput::horizontalAlignment
340 \qmlproperty enumeration QtQuick2::TextInput::effectiveHorizontalAlignment
342 Sets the horizontal alignment of the text within the TextInput item's
343 width and height. By default, the text alignment follows the natural alignment
344 of the text, for example text that is read from left to right will be aligned to
347 TextInput does not have vertical alignment, as the natural height is
348 exactly the height of the single line of text. If you set the height
349 manually to something larger, TextInput will always be top aligned
350 vertically. You can use anchors to align it however you want within
353 The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and
354 \c TextInput.AlignHCenter.
356 When using the attached property LayoutMirroring::enabled to mirror application
357 layouts, the horizontal alignment of text will also be mirrored. However, the property
358 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
359 of TextInput, use the read-only property \c effectiveHorizontalAlignment.
361 QQuickTextInput::HAlignment QQuickTextInput::hAlign() const
363 Q_D(const QQuickTextInput);
367 void QQuickTextInput::setHAlign(HAlignment align)
369 Q_D(QQuickTextInput);
370 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
371 d->hAlignImplicit = false;
372 if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
373 updateCursorRectangle();
377 void QQuickTextInput::resetHAlign()
379 Q_D(QQuickTextInput);
380 d->hAlignImplicit = true;
381 if (d->determineHorizontalAlignment() && isComponentComplete()) {
382 updateCursorRectangle();
386 QQuickTextInput::HAlignment QQuickTextInput::effectiveHAlign() const
388 Q_D(const QQuickTextInput);
389 QQuickTextInput::HAlignment effectiveAlignment = d->hAlign;
390 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
392 case QQuickTextInput::AlignLeft:
393 effectiveAlignment = QQuickTextInput::AlignRight;
395 case QQuickTextInput::AlignRight:
396 effectiveAlignment = QQuickTextInput::AlignLeft;
402 return effectiveAlignment;
405 bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment alignment, bool forceAlign)
407 Q_Q(QQuickTextInput);
408 if ((hAlign != alignment || forceAlign) && alignment <= QQuickTextInput::AlignHCenter) { // justify not supported
409 QQuickTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
411 emit q->horizontalAlignmentChanged(alignment);
412 if (oldEffectiveHAlign != q->effectiveHAlign())
413 emit q->effectiveHorizontalAlignmentChanged();
419 bool QQuickTextInputPrivate::determineHorizontalAlignment()
421 if (hAlignImplicit) {
422 // if no explicit alignment has been set, follow the natural layout direction of the text
423 QString text = q_func()->text();
425 text = m_textLayout.preeditAreaText();
426 bool isRightToLeft = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
427 return setHAlign(isRightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
432 void QQuickTextInputPrivate::mirrorChange()
434 Q_Q(QQuickTextInput);
435 if (q->isComponentComplete()) {
436 if (!hAlignImplicit && (hAlign == QQuickTextInput::AlignRight || hAlign == QQuickTextInput::AlignLeft)) {
437 q->updateCursorRectangle();
438 emit q->effectiveHorizontalAlignmentChanged();
444 \qmlproperty bool QtQuick2::TextInput::readOnly
446 Sets whether user input can modify the contents of the TextInput.
448 If readOnly is set to true, then user input will not affect the text
449 property. Any bindings or attempts to set the text property will still
452 bool QQuickTextInput::isReadOnly() const
454 Q_D(const QQuickTextInput);
455 return d->m_readOnly;
458 void QQuickTextInput::setReadOnly(bool ro)
460 Q_D(QQuickTextInput);
461 if (d->m_readOnly == ro)
464 setFlag(QQuickItem::ItemAcceptsInputMethod, !ro);
467 d->setCursorPosition(d->end());
469 emit readOnlyChanged(ro);
473 \qmlproperty int QtQuick2::TextInput::maximumLength
474 The maximum permitted length of the text in the TextInput.
476 If the text is too long, it is truncated at the limit.
478 By default, this property contains a value of 32767.
480 int QQuickTextInput::maxLength() const
482 Q_D(const QQuickTextInput);
483 return d->m_maxLength;
486 void QQuickTextInput::setMaxLength(int ml)
488 Q_D(QQuickTextInput);
489 if (d->m_maxLength == ml || d->m_maskData)
493 d->internalSetText(d->m_text, -1, false);
495 emit maximumLengthChanged(ml);
499 \qmlproperty bool QtQuick2::TextInput::cursorVisible
500 Set to true when the TextInput shows a cursor.
502 This property is set and unset when the TextInput gets active focus, so that other
503 properties can be bound to whether the cursor is currently showing. As it
504 gets set and unset automatically, when you set the value yourself you must
505 keep in mind that your value may be overwritten.
507 It can be set directly in script, for example if a KeyProxy might
508 forward keys to it and you desire it to look active when this happens
509 (but without actually giving it active focus).
511 It should not be set directly on the element, like in the below QML,
512 as the specified value will be overridden an lost on focus changes.
521 In the above snippet the cursor will still become visible when the
522 TextInput gains active focus.
524 bool QQuickTextInput::isCursorVisible() const
526 Q_D(const QQuickTextInput);
527 return d->cursorVisible;
530 void QQuickTextInput::setCursorVisible(bool on)
532 Q_D(QQuickTextInput);
533 if (d->cursorVisible == on)
535 d->cursorVisible = on;
536 d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
537 QRect r = cursorRectangle();
538 if (d->inputMask().isEmpty())
542 emit cursorVisibleChanged(d->cursorVisible);
546 \qmlproperty int QtQuick2::TextInput::cursorPosition
547 The position of the cursor in the TextInput.
549 int QQuickTextInput::cursorPosition() const
551 Q_D(const QQuickTextInput);
555 void QQuickTextInput::setCursorPosition(int cp)
557 Q_D(QQuickTextInput);
558 if (cp < 0 || cp > text().length())
564 Returns a Rect which encompasses the cursor, but which may be larger than is
565 required. Ignores custom cursor delegates.
567 QRect QQuickTextInput::cursorRectangle() const
569 Q_D(const QQuickTextInput);
570 QTextLine l = d->m_textLayout.lineAt(0);
572 if (d->m_preeditCursor != -1)
573 c += d->m_preeditCursor;
574 return QRect(qRound(l.cursorToX(c)) - d->hscroll, 0, d->m_cursorWidth, l.height());
577 \qmlproperty int QtQuick2::TextInput::selectionStart
579 The cursor position before the first character in the current selection.
581 This property is read-only. To change the selection, use select(start,end),
582 selectAll(), or selectWord().
584 \sa selectionEnd, cursorPosition, selectedText
586 int QQuickTextInput::selectionStart() const
588 Q_D(const QQuickTextInput);
589 return d->lastSelectionStart;
592 \qmlproperty int QtQuick2::TextInput::selectionEnd
594 The cursor position after the last character in the current selection.
596 This property is read-only. To change the selection, use select(start,end),
597 selectAll(), or selectWord().
599 \sa selectionStart, cursorPosition, selectedText
601 int QQuickTextInput::selectionEnd() const
603 Q_D(const QQuickTextInput);
604 return d->lastSelectionEnd;
607 \qmlmethod void QtQuick2::TextInput::select(int start, int end)
609 Causes the text from \a start to \a end to be selected.
611 If either start or end is out of range, the selection is not changed.
613 After calling this, selectionStart will become the lesser
614 and selectionEnd will become the greater (regardless of the order passed
617 \sa selectionStart, selectionEnd
619 void QQuickTextInput::select(int start, int end)
621 Q_D(QQuickTextInput);
622 if (start < 0 || end < 0 || start > text().length() || end > text().length())
624 d->setSelection(start, end-start);
628 \qmlproperty string QtQuick2::TextInput::selectedText
630 This read-only property provides the text currently selected in the
633 It is equivalent to the following snippet, but is faster and easier
637 myTextInput.text.toString().substring(myTextInput.selectionStart,
638 myTextInput.selectionEnd);
641 QString QQuickTextInput::selectedText() const
643 Q_D(const QQuickTextInput);
644 return d->selectedText();
648 \qmlproperty bool QtQuick2::TextInput::activeFocusOnPress
650 Whether the TextInput should gain active focus on a mouse press. By default this is
653 bool QQuickTextInput::focusOnPress() const
655 Q_D(const QQuickTextInput);
656 return d->focusOnPress;
659 void QQuickTextInput::setFocusOnPress(bool b)
661 Q_D(QQuickTextInput);
662 if (d->focusOnPress == b)
667 emit activeFocusOnPressChanged(d->focusOnPress);
670 \qmlproperty bool QtQuick2::TextInput::autoScroll
672 Whether the TextInput should scroll when the text is longer than the width. By default this is
675 bool QQuickTextInput::autoScroll() const
677 Q_D(const QQuickTextInput);
678 return d->autoScroll;
681 void QQuickTextInput::setAutoScroll(bool b)
683 Q_D(QQuickTextInput);
684 if (d->autoScroll == b)
688 //We need to repaint so that the scrolling is taking into account.
690 updateCursorRectangle();
691 emit autoScrollChanged(d->autoScroll);
694 #ifndef QT_NO_VALIDATOR
697 \qmlclass IntValidator QIntValidator
698 \inqmlmodule QtQuick 2
699 \ingroup qml-basic-visual-elements
701 This element provides a validator for integer values.
703 IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and
704 will accept locale specific digits, group separators, and positive and negative signs. In
705 addition, IntValidator is always guaranteed to accept a number formatted according to the "C"
709 \qmlproperty int QtQuick2::IntValidator::top
711 This property holds the validator's highest acceptable value.
712 By default, this property's value is derived from the highest signed integer available (typically 2147483647).
715 \qmlproperty int QtQuick2::IntValidator::bottom
717 This property holds the validator's lowest acceptable value.
718 By default, this property's value is derived from the lowest signed integer available (typically -2147483647).
722 \qmlclass DoubleValidator QDoubleValidator
723 \inqmlmodule QtQuick 2
724 \ingroup qml-basic-visual-elements
726 This element provides a validator for non-integer numbers.
730 \qmlproperty real QtQuick2::DoubleValidator::top
732 This property holds the validator's maximum acceptable value.
733 By default, this property contains a value of infinity.
736 \qmlproperty real QtQuick2::DoubleValidator::bottom
738 This property holds the validator's minimum acceptable value.
739 By default, this property contains a value of -infinity.
742 \qmlproperty int QtQuick2::DoubleValidator::decimals
744 This property holds the validator's maximum number of digits after the decimal point.
745 By default, this property contains a value of 1000.
748 \qmlproperty enumeration QtQuick2::DoubleValidator::notation
749 This property holds the notation of how a string can describe a number.
751 The possible values for this property are:
754 \o DoubleValidator.StandardNotation
755 \o DoubleValidator.ScientificNotation (default)
758 If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2).
762 \qmlclass RegExpValidator QRegExpValidator
763 \inqmlmodule QtQuick 2
764 \ingroup qml-basic-visual-elements
766 This element provides a validator, which counts as valid any string which
767 matches a specified regular expression.
770 \qmlproperty regExp QtQuick2::RegExpValidator::regExp
772 This property holds the regular expression used for validation.
774 Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression
777 By default, this property contains a regular expression with the pattern .* that matches any string.
781 \qmlproperty Validator QtQuick2::TextInput::validator
783 Allows you to set a validator on the TextInput. When a validator is set
784 the TextInput will only accept input which leaves the text property in
785 an acceptable or intermediate state. The accepted signal will only be sent
786 if the text is in an acceptable state when enter is pressed.
788 Currently supported validators are IntValidator, DoubleValidator and
789 RegExpValidator. An example of using validators is shown below, which allows
790 input of integers between 11 and 31 into the text input:
795 validator: IntValidator{bottom: 11; top: 31;}
800 \sa acceptableInput, inputMask
803 QValidator* QQuickTextInput::validator() const
805 Q_D(const QQuickTextInput);
806 return d->m_validator;
809 void QQuickTextInput::setValidator(QValidator* v)
811 Q_D(QQuickTextInput);
812 if (d->m_validator == v)
816 if (!d->hasAcceptableInput(d->m_text)) {
817 d->oldValidity = false;
818 emit acceptableInputChanged();
821 emit validatorChanged();
823 #endif // QT_NO_VALIDATOR
826 \qmlproperty string QtQuick2::TextInput::inputMask
828 Allows you to set an input mask on the TextInput, restricting the allowable
829 text inputs. See QLineEdit::inputMask for further details, as the exact
830 same mask strings are used by TextInput.
832 \sa acceptableInput, validator
834 QString QQuickTextInput::inputMask() const
836 Q_D(const QQuickTextInput);
837 return d->inputMask();
840 void QQuickTextInput::setInputMask(const QString &im)
842 Q_D(QQuickTextInput);
843 if (d->inputMask() == im)
847 emit inputMaskChanged(d->inputMask());
851 \qmlproperty bool QtQuick2::TextInput::acceptableInput
853 This property is always true unless a validator or input mask has been set.
854 If a validator or input mask has been set, this property will only be true
855 if the current text is acceptable to the validator or input mask as a final
856 string (not as an intermediate string).
858 bool QQuickTextInput::hasAcceptableInput() const
860 Q_D(const QQuickTextInput);
861 return d->hasAcceptableInput(d->m_text);
865 \qmlsignal QtQuick2::TextInput::onAccepted()
867 This handler is called when the Return or Enter key is pressed.
868 Note that if there is a \l validator or \l inputMask set on the text
869 input, the handler will only be emitted if the input is in an acceptable
873 void QQuickTextInputPrivate::updateInputMethodHints()
875 Q_Q(QQuickTextInput);
876 Qt::InputMethodHints hints = inputMethodHints;
877 if (m_echoMode == QQuickTextInput::Password || m_echoMode == QQuickTextInput::NoEcho)
878 hints |= Qt::ImhHiddenText;
879 else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
880 hints &= ~Qt::ImhHiddenText;
881 if (m_echoMode != QQuickTextInput::Normal)
882 hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
883 q->setInputMethodHints(hints);
886 \qmlproperty enumeration QtQuick2::TextInput::echoMode
888 Specifies how the text should be displayed in the TextInput.
890 \o TextInput.Normal - Displays the text as it is. (Default)
891 \o TextInput.Password - Displays asterisks instead of characters.
892 \o TextInput.NoEcho - Displays nothing.
893 \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered
894 while editing, otherwise displays asterisks.
897 QQuickTextInput::EchoMode QQuickTextInput::echoMode() const
899 Q_D(const QQuickTextInput);
900 return QQuickTextInput::EchoMode(d->m_echoMode);
903 void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
905 Q_D(QQuickTextInput);
906 if (echoMode() == echo)
908 d->m_echoMode = echo;
909 d->m_passwordEchoEditing = false;
910 d->updateInputMethodHints();
912 d->updateDisplayText();
915 emit echoModeChanged(echoMode());
918 Qt::InputMethodHints QQuickTextInput::imHints() const
920 Q_D(const QQuickTextInput);
921 return d->inputMethodHints;
924 void QQuickTextInput::setIMHints(Qt::InputMethodHints hints)
926 Q_D(QQuickTextInput);
927 if (d->inputMethodHints == hints)
929 d->inputMethodHints = hints;
930 d->updateInputMethodHints();
934 \qmlproperty Component QtQuick2::TextInput::cursorDelegate
935 The delegate for the cursor in the TextInput.
937 If you set a cursorDelegate for a TextInput, this delegate will be used for
938 drawing the cursor instead of the standard cursor. An instance of the
939 delegate will be created and managed by the TextInput when a cursor is
940 needed, and the x property of delegate instance will be set so as
941 to be one pixel before the top left of the current character.
943 Note that the root item of the delegate component must be a QDeclarativeItem or
944 QDeclarativeItem derived item.
946 QDeclarativeComponent* QQuickTextInput::cursorDelegate() const
948 Q_D(const QQuickTextInput);
949 return d->cursorComponent;
952 void QQuickTextInput::setCursorDelegate(QDeclarativeComponent* c)
954 Q_D(QQuickTextInput);
955 if (d->cursorComponent == c)
958 d->cursorComponent = c;
960 //note that the components are owned by something else
961 delete d->cursorItem;
963 d->startCreatingCursor();
966 emit cursorDelegateChanged();
969 void QQuickTextInputPrivate::startCreatingCursor()
971 Q_Q(QQuickTextInput);
972 if (cursorComponent->isReady()) {
974 } else if (cursorComponent->isLoading()) {
975 q->connect(cursorComponent, SIGNAL(statusChanged(int)),
976 q, SLOT(createCursor()));
978 qmlInfo(q, cursorComponent->errors()) << QQuickTextInput::tr("Could not load cursor delegate");
982 void QQuickTextInput::createCursor()
984 Q_D(QQuickTextInput);
985 if (d->cursorComponent->isError()) {
986 qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
990 if (!d->cursorComponent->isReady())
994 delete d->cursorItem;
995 QDeclarativeContext *creationContext = d->cursorComponent->creationContext();
996 QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this));
997 d->cursorItem = qobject_cast<QQuickItem*>(object);
998 if (!d->cursorItem) {
1000 qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
1004 QDeclarative_setParent_noEvent(d->cursorItem, this);
1005 d->cursorItem->setParentItem(this);
1006 d->cursorItem->setX(d->cursorToX());
1007 d->cursorItem->setHeight(d->calculateTextHeight());
1011 \qmlmethod rect QtQuick2::TextInput::positionToRectangle(int pos)
1013 This function takes a character position and returns the rectangle that the
1014 cursor would occupy, if it was placed at that character position.
1016 This is similar to setting the cursorPosition, and then querying the cursor
1017 rectangle, but the cursorPosition is not changed.
1019 QRectF QQuickTextInput::positionToRectangle(int pos) const
1021 Q_D(const QQuickTextInput);
1022 if (pos > d->m_cursor)
1023 pos += d->preeditAreaText().length();
1024 QTextLine l = d->m_textLayout.lineAt(0);
1025 return QRectF( l.cursorToX(pos) - d->hscroll, 0.0, d->m_cursorWidth, l.height());
1029 \qmlmethod int QtQuick2::TextInput::positionAt(int x, CursorPosition position = CursorBetweenCharacters)
1031 This function returns the character position at
1032 x pixels from the left of the textInput. Position 0 is before the
1033 first character, position 1 is after the first character but before the second,
1034 and so on until position text.length, which is after all characters.
1036 This means that for all x values before the first character this function returns 0,
1037 and for all x values after the last character this function returns text.length.
1039 The cursor position type specifies how the cursor position should be resolved.
1042 \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x.
1043 \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
1046 int QQuickTextInput::positionAt(int x) const
1048 return positionAt(x, CursorBetweenCharacters);
1051 int QQuickTextInput::positionAt(int x, CursorPosition position) const
1053 Q_D(const QQuickTextInput);
1054 int pos = d->m_textLayout.lineAt(0).xToCursor(x + d->hscroll, QTextLine::CursorPosition(position));
1055 const int cursor = d->m_cursor;
1057 const int preeditLength = d->preeditAreaText().length();
1058 pos = pos > cursor + preeditLength
1059 ? pos - preeditLength
1065 void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
1067 Q_D(QQuickTextInput);
1068 // Don't allow MacOSX up/down support, and we don't allow a completer.
1069 bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier;
1070 if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
1071 // Ignore when moving off the end unless there is a selection,
1072 // because then moving will do something (deselect).
1073 int cursorPosition = d->m_cursor;
1074 if (cursorPosition == 0)
1075 ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
1076 if (cursorPosition == text().length())
1077 ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
1082 d->processKeyEvent(ev);
1084 if (!ev->isAccepted())
1085 QQuickImplicitSizeItem::keyPressEvent(ev);
1088 void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
1090 Q_D(QQuickTextInput);
1091 const bool wasComposing = d->preeditAreaText().length() > 0;
1092 if (d->m_readOnly) {
1095 d->processInputMethodEvent(ev);
1097 if (!ev->isAccepted())
1098 QQuickImplicitSizeItem::inputMethodEvent(ev);
1100 if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0))
1101 emit inputMethodComposingChanged();
1104 void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event)
1106 Q_D(QQuickTextInput);
1108 if (d->selectByMouse && event->button() == Qt::LeftButton) {
1110 int cursor = d->xToPos(event->localPos().x());
1111 d->selectWordAtPos(cursor);
1112 event->setAccepted(true);
1113 if (!d->hasPendingTripleClick()) {
1114 d->tripleClickStartPoint = event->localPos().toPoint();
1115 d->tripleClickTimer.start();
1118 if (d->sendMouseEventToInputContext(event))
1120 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
1124 void QQuickTextInput::mousePressEvent(QMouseEvent *event)
1126 Q_D(QQuickTextInput);
1128 d->pressPos = event->localPos();
1130 if (d->focusOnPress) {
1131 bool hadActiveFocus = hasActiveFocus();
1133 // re-open input panel on press if already focused
1134 if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
1135 openSoftwareInputPanel();
1137 if (d->selectByMouse) {
1138 setKeepMouseGrab(false);
1139 d->selectPressed = true;
1140 QPoint distanceVector = d->pressPos.toPoint() - d->tripleClickStartPoint;
1141 if (d->hasPendingTripleClick()
1142 && distanceVector.manhattanLength() < qApp->styleHints()->startDragDistance()) {
1143 event->setAccepted(true);
1149 if (d->sendMouseEventToInputContext(event))
1152 bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
1153 int cursor = d->xToPos(event->localPos().x());
1154 d->moveCursor(cursor, mark);
1155 event->setAccepted(true);
1158 void QQuickTextInput::mouseMoveEvent(QMouseEvent *event)
1160 Q_D(QQuickTextInput);
1162 if (d->selectPressed) {
1163 if (qAbs(int(event->localPos().x() - d->pressPos.x())) > qApp->styleHints()->startDragDistance())
1164 setKeepMouseGrab(true);
1166 if (d->composeMode()) {
1168 int startPos = d->xToPos(d->pressPos.x());
1169 int currentPos = d->xToPos(event->localPos().x());
1170 if (startPos != currentPos)
1171 d->setSelection(startPos, currentPos - startPos);
1173 moveCursorSelection(d->xToPos(event->localPos().x()), d->mouseSelectionMode);
1175 event->setAccepted(true);
1177 QQuickImplicitSizeItem::mouseMoveEvent(event);
1181 void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
1183 Q_D(QQuickTextInput);
1184 if (d->sendMouseEventToInputContext(event))
1186 if (d->selectPressed) {
1187 d->selectPressed = false;
1188 setKeepMouseGrab(false);
1190 #ifndef QT_NO_CLIPBOARD
1191 if (QGuiApplication::clipboard()->supportsSelection()) {
1192 if (event->button() == Qt::LeftButton) {
1193 d->copy(QClipboard::Selection);
1194 } else if (!d->m_readOnly && event->button() == Qt::MidButton) {
1196 d->insert(QGuiApplication::clipboard()->text(QClipboard::Selection));
1200 if (!event->isAccepted())
1201 QQuickImplicitSizeItem::mouseReleaseEvent(event);
1204 bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event)
1206 #if !defined QT_NO_IM
1207 if (composeMode()) {
1208 int tmp_cursor = xToPos(event->localPos().x());
1209 int mousePos = tmp_cursor - m_cursor;
1210 if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().length()) {
1211 if (event->type() == QEvent::MouseButtonRelease) {
1212 qApp->inputPanel()->invokeAction(QInputPanel::Click, mousePos);
1225 void QQuickTextInput::mouseUngrabEvent()
1227 Q_D(QQuickTextInput);
1228 d->selectPressed = false;
1229 setKeepMouseGrab(false);
1232 bool QQuickTextInput::event(QEvent* ev)
1234 #ifndef QT_NO_SHORTCUT
1235 Q_D(QQuickTextInput);
1236 if (ev->type() == QEvent::ShortcutOverride) {
1239 QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
1240 if (ke == QKeySequence::Copy
1241 || ke == QKeySequence::Paste
1242 || ke == QKeySequence::Cut
1243 || ke == QKeySequence::Redo
1244 || ke == QKeySequence::Undo
1245 || ke == QKeySequence::MoveToNextWord
1246 || ke == QKeySequence::MoveToPreviousWord
1247 || ke == QKeySequence::MoveToStartOfDocument
1248 || ke == QKeySequence::MoveToEndOfDocument
1249 || ke == QKeySequence::SelectNextWord
1250 || ke == QKeySequence::SelectPreviousWord
1251 || ke == QKeySequence::SelectStartOfLine
1252 || ke == QKeySequence::SelectEndOfLine
1253 || ke == QKeySequence::SelectStartOfBlock
1254 || ke == QKeySequence::SelectEndOfBlock
1255 || ke == QKeySequence::SelectStartOfDocument
1256 || ke == QKeySequence::SelectAll
1257 || ke == QKeySequence::SelectEndOfDocument) {
1259 } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1260 || ke->modifiers() == Qt::KeypadModifier) {
1261 if (ke->key() < Qt::Key_Escape) {
1265 switch (ke->key()) {
1266 case Qt::Key_Delete:
1269 case Qt::Key_Backspace:
1281 return QQuickImplicitSizeItem::event(ev);
1284 void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
1285 const QRectF &oldGeometry)
1287 if (newGeometry.width() != oldGeometry.width()) {
1289 updateCursorRectangle();
1291 QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
1294 void QQuickTextInputPrivate::updateHorizontalScroll()
1296 Q_Q(QQuickTextInput);
1297 const int preeditLength = m_textLayout.preeditAreaText().length();
1298 const int width = q->width();
1299 int widthUsed = calculateTextWidth();
1301 if (!autoScroll || widthUsed <= width) {
1302 QQuickTextInput::HAlignment effectiveHAlign = q->effectiveHAlign();
1303 // text fits in br; use hscroll for alignment
1304 switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) {
1305 case Qt::AlignRight:
1306 hscroll = widthUsed - width;
1308 case Qt::AlignHCenter:
1309 hscroll = (widthUsed - width) / 2;
1317 int cix = qRound(cursorToX(m_cursor + preeditLength));
1318 if (cix - hscroll >= width) {
1319 // text doesn't fit, cursor is to the right of br (scroll right)
1320 hscroll = cix - width;
1321 } else if (cix - hscroll < 0 && hscroll < widthUsed) {
1322 // text doesn't fit, cursor is to the left of br (scroll left)
1324 } else if (widthUsed - hscroll < width) {
1325 // text doesn't fit, text document is to the left of br; align
1327 hscroll = widthUsed - width;
1329 if (preeditLength > 0) {
1330 // check to ensure long pre-edit text doesn't push the cursor
1332 cix = qRound(cursorToX(
1333 m_cursor + qMax(0, m_preeditCursor - 1)));
1340 QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1343 Q_D(QQuickTextInput);
1345 QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
1347 node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
1350 if (!d->textLayoutDirty) {
1351 QSGSimpleRectNode *cursorNode = node->cursorNode();
1352 if (cursorNode != 0 && !isReadOnly()) {
1353 cursorNode->setRect(cursorRectangle());
1355 if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
1362 node->deleteContent();
1363 node->setMatrix(QMatrix4x4());
1365 QPoint offset = QPoint(0,0);
1366 QFontMetrics fm = QFontMetrics(d->font);
1367 QRect br(boundingRect().toRect());
1368 if (d->autoScroll) {
1369 // the y offset is there to keep the baseline constant in case we have script changes in the text.
1370 offset = br.topLeft() - QPoint(d->hscroll, d->ascent() - fm.ascent());
1372 offset = QPoint(d->hscroll, 0);
1375 if (!d->m_textLayout.text().isEmpty()) {
1376 node->addTextLayout(offset, &d->m_textLayout, d->color,
1377 QQuickText::Normal, QColor(),
1378 d->selectionColor, d->selectedTextColor,
1379 d->selectionStart(),
1380 d->selectionEnd() - 1); // selectionEnd() returns first char after
1384 if (!isReadOnly() && d->cursorItem == 0) {
1385 node->setCursor(cursorRectangle(), d->color);
1386 if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
1393 d->textLayoutDirty = false;
1399 QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
1401 Q_D(const QQuickTextInput);
1404 return QVariant((bool)(flags() & ItemAcceptsInputMethod));
1406 return QVariant((int)inputMethodHints());
1407 case Qt::ImCursorRectangle:
1408 return cursorRectangle();
1411 case Qt::ImCursorPosition:
1412 return QVariant(d->m_cursor);
1413 case Qt::ImSurroundingText:
1414 if (d->m_echoMode == PasswordEchoOnEdit && !d->m_passwordEchoEditing) {
1415 return QVariant(displayText());
1417 return QVariant(d->realText());
1419 case Qt::ImCurrentSelection:
1420 return QVariant(selectedText());
1421 case Qt::ImMaximumTextLength:
1422 return QVariant(maxLength());
1423 case Qt::ImAnchorPosition:
1424 if (d->selectionStart() == d->selectionEnd())
1425 return QVariant(d->m_cursor);
1426 else if (d->selectionStart() == d->m_cursor)
1427 return QVariant(d->selectionEnd());
1429 return QVariant(d->selectionStart());
1436 \qmlmethod void QtQuick2::TextInput::deselect()
1438 Removes active text selection.
1440 void QQuickTextInput::deselect()
1442 Q_D(QQuickTextInput);
1447 \qmlmethod void QtQuick2::TextInput::selectAll()
1449 Causes all text to be selected.
1451 void QQuickTextInput::selectAll()
1453 Q_D(QQuickTextInput);
1454 d->setSelection(0, text().length());
1458 \qmlmethod void QtQuick2::TextInput::isRightToLeft(int start, int end)
1460 Returns true if the natural reading direction of the editor text
1461 found between positions \a start and \a end is right to left.
1463 bool QQuickTextInput::isRightToLeft(int start, int end)
1466 qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
1469 return text().mid(start, end - start).isRightToLeft();
1473 #ifndef QT_NO_CLIPBOARD
1475 \qmlmethod QtQuick2::TextInput::cut()
1477 Moves the currently selected text to the system clipboard.
1479 void QQuickTextInput::cut()
1481 Q_D(QQuickTextInput);
1487 \qmlmethod QtQuick2::TextInput::copy()
1489 Copies the currently selected text to the system clipboard.
1491 void QQuickTextInput::copy()
1493 Q_D(QQuickTextInput);
1498 \qmlmethod QtQuick2::TextInput::paste()
1500 Replaces the currently selected text by the contents of the system clipboard.
1502 void QQuickTextInput::paste()
1504 Q_D(QQuickTextInput);
1508 #endif // QT_NO_CLIPBOARD
1511 \qmlmethod void QtQuick2::TextInput::selectWord()
1513 Causes the word closest to the current cursor position to be selected.
1515 void QQuickTextInput::selectWord()
1517 Q_D(QQuickTextInput);
1518 d->selectWordAtPos(d->m_cursor);
1522 \qmlproperty bool QtQuick2::TextInput::smooth
1524 This property holds whether the text is smoothly scaled or transformed.
1526 Smooth filtering gives better visual quality, but is slower. If
1527 the item is displayed at its natural size, this property has no visual or
1530 \note Generally scaling artifacts are only visible if the item is stationary on
1531 the screen. A common pattern when animating an item is to disable smooth
1532 filtering at the beginning of the animation and reenable it at the conclusion.
1536 \qmlproperty string QtQuick2::TextInput::passwordCharacter
1538 This is the character displayed when echoMode is set to Password or
1539 PasswordEchoOnEdit. By default it is an asterisk.
1541 If this property is set to a string with more than one character,
1542 the first character is used. If the string is empty, the value
1543 is ignored and the property is not set.
1545 QString QQuickTextInput::passwordCharacter() const
1547 Q_D(const QQuickTextInput);
1548 return QString(d->m_passwordCharacter);
1551 void QQuickTextInput::setPasswordCharacter(const QString &str)
1553 Q_D(QQuickTextInput);
1554 if (str.length() < 1)
1556 d->m_passwordCharacter = str.constData()[0];
1557 d->updateDisplayText();
1558 if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit) {
1561 emit passwordCharacterChanged();
1565 \qmlproperty string QtQuick2::TextInput::displayText
1567 This is the text displayed in the TextInput.
1569 If \l echoMode is set to TextInput::Normal, this holds the
1570 same value as the TextInput::text property. Otherwise,
1571 this property holds the text visible to the user, while
1572 the \l text property holds the actual entered text.
1574 QString QQuickTextInput::displayText() const
1576 Q_D(const QQuickTextInput);
1577 return d->m_textLayout.text();
1581 \qmlproperty bool QtQuick2::TextInput::selectByMouse
1585 If true, the user can use the mouse to select text in some
1586 platform-specific way. Note that for some platforms this may
1587 not be an appropriate interaction (eg. may conflict with how
1588 the text needs to behave inside a Flickable.
1590 bool QQuickTextInput::selectByMouse() const
1592 Q_D(const QQuickTextInput);
1593 return d->selectByMouse;
1596 void QQuickTextInput::setSelectByMouse(bool on)
1598 Q_D(QQuickTextInput);
1599 if (d->selectByMouse != on) {
1600 d->selectByMouse = on;
1601 emit selectByMouseChanged(on);
1606 \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode
1608 Specifies how text should be selected using a mouse.
1611 \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
1612 \o TextInput.SelectWords - The selection is updated with whole words.
1615 This property only applies when \l selectByMouse is true.
1618 QQuickTextInput::SelectionMode QQuickTextInput::mouseSelectionMode() const
1620 Q_D(const QQuickTextInput);
1621 return d->mouseSelectionMode;
1624 void QQuickTextInput::setMouseSelectionMode(SelectionMode mode)
1626 Q_D(QQuickTextInput);
1627 if (d->mouseSelectionMode != mode) {
1628 d->mouseSelectionMode = mode;
1629 emit mouseSelectionModeChanged(mode);
1634 \qmlproperty bool QtQuick2::TextInput::canPaste
1636 Returns true if the TextInput is writable and the content of the clipboard is
1637 suitable for pasting into the TextEdit.
1639 bool QQuickTextInput::canPaste() const
1641 Q_D(const QQuickTextInput);
1645 void QQuickTextInput::moveCursorSelection(int position)
1647 Q_D(QQuickTextInput);
1648 d->moveCursor(position, true);
1652 \qmlmethod void QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters)
1654 Moves the cursor to \a position and updates the selection according to the optional \a mode
1655 parameter. (To only move the cursor, set the \l cursorPosition property.)
1657 When this method is called it additionally sets either the
1658 selectionStart or the selectionEnd (whichever was at the previous cursor position)
1659 to the specified position. This allows you to easily extend and contract the selected
1662 The selection mode specifies whether the selection is updated on a per character or a per word
1663 basis. If not specified the selection mode will default to TextInput.SelectCharacters.
1666 \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
1667 the previous cursor position) to the specified position.
1668 \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
1669 words between the specified position and the previous cursor position. Words partially in the
1673 For example, take this sequence of calls:
1677 moveCursorSelection(9, TextInput.SelectCharacters)
1678 moveCursorSelection(7, TextInput.SelectCharacters)
1681 This moves the cursor to position 5, extend the selection end from 5 to 9
1682 and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
1683 selected (the 6th and 7th characters).
1685 The same sequence with TextInput.SelectWords will extend the selection start to a word boundary
1686 before or on position 5 and extend the selection end to a word boundary on or past position 9.
1688 void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
1690 Q_D(QQuickTextInput);
1692 if (mode == SelectCharacters) {
1693 d->moveCursor(pos, true);
1694 } else if (pos != d->m_cursor){
1695 const int cursor = d->m_cursor;
1697 if (!d->hasSelectedText())
1698 anchor = d->m_cursor;
1699 else if (d->selectionStart() == d->m_cursor)
1700 anchor = d->selectionEnd();
1702 anchor = d->selectionStart();
1704 if (anchor < pos || (anchor == pos && cursor < pos)) {
1705 const QString text = this->text();
1706 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1707 finder.setPosition(anchor);
1709 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1710 if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord)
1711 || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) {
1712 finder.toPreviousBoundary();
1714 anchor = finder.position() != -1 ? finder.position() : 0;
1716 finder.setPosition(pos);
1717 if (pos > 0 && !finder.boundaryReasons())
1718 finder.toNextBoundary();
1719 const int cursor = finder.position() != -1 ? finder.position() : text.length();
1721 d->setSelection(anchor, cursor - anchor);
1722 } else if (anchor > pos || (anchor == pos && cursor > pos)) {
1723 const QString text = this->text();
1724 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1725 finder.setPosition(anchor);
1727 const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1728 if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord)
1729 || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) {
1730 finder.toNextBoundary();
1733 anchor = finder.position() != -1 ? finder.position() : text.length();
1735 finder.setPosition(pos);
1736 if (pos < text.length() && !finder.boundaryReasons())
1737 finder.toPreviousBoundary();
1738 const int cursor = finder.position() != -1 ? finder.position() : 0;
1740 d->setSelection(anchor, cursor - anchor);
1746 \qmlmethod void QtQuick2::TextInput::openSoftwareInputPanel()
1748 Opens software input panels like virtual keyboards for typing, useful for
1749 customizing when you want the input keyboard to be shown and hidden in
1752 By default the opening of input panels follows the platform style. Input panels are
1753 always closed if no editor has active focus.
1755 You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1756 and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1757 the behavior you want.
1759 Only relevant on platforms, which provide virtual keyboards.
1765 text: "Hello world!"
1766 activeFocusOnPress: false
1768 anchors.fill: parent
1770 if (!textInput.activeFocus) {
1771 textInput.forceActiveFocus()
1772 textInput.openSoftwareInputPanel();
1774 textInput.focus = false;
1777 onPressAndHold: textInput.closeSoftwareInputPanel();
1782 void QQuickTextInput::openSoftwareInputPanel()
1785 qGuiApp->inputPanel()->show();
1789 \qmlmethod void QtQuick2::TextInput::closeSoftwareInputPanel()
1791 Closes a software input panel like a virtual keyboard shown on the screen, useful
1792 for customizing when you want the input keyboard to be shown and hidden in
1795 By default the opening of input panels follows the platform style. Input panels are
1796 always closed if no editor has active focus.
1798 You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1799 and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1800 the behavior you want.
1802 Only relevant on platforms, which provide virtual keyboards.
1808 text: "Hello world!"
1809 activeFocusOnPress: false
1811 anchors.fill: parent
1813 if (!textInput.activeFocus) {
1814 textInput.forceActiveFocus();
1815 textInput.openSoftwareInputPanel();
1817 textInput.focus = false;
1820 onPressAndHold: textInput.closeSoftwareInputPanel();
1825 void QQuickTextInput::closeSoftwareInputPanel()
1828 qGuiApp->inputPanel()->hide();
1831 void QQuickTextInput::focusInEvent(QFocusEvent *event)
1833 Q_D(const QQuickTextInput);
1834 if (d->focusOnPress && !d->m_readOnly)
1835 openSoftwareInputPanel();
1836 QQuickImplicitSizeItem::focusInEvent(event);
1839 void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
1841 Q_D(QQuickTextInput);
1842 if (change == ItemActiveFocusHasChanged) {
1843 bool hasFocus = value.boolValue;
1844 d->focused = hasFocus;
1845 setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus()
1846 if (echoMode() == QQuickTextInput::PasswordEchoOnEdit && !hasFocus)
1847 d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
1851 QQuickItem::itemChange(change, value);
1855 \qmlproperty bool QtQuick2::TextInput::inputMethodComposing
1858 This property holds whether the TextInput has partial text input from an
1861 While it is composing an input method may rely on mouse or key events from
1862 the TextInput to edit or commit the partial text. This property can be
1863 used to determine when to disable events handlers that may interfere with
1864 the correct operation of an input method.
1866 bool QQuickTextInput::isInputMethodComposing() const
1868 Q_D(const QQuickTextInput);
1869 return d->preeditAreaText().length() > 0;
1872 void QQuickTextInputPrivate::init()
1874 Q_Q(QQuickTextInput);
1875 q->setSmooth(smooth);
1876 q->setAcceptedMouseButtons(Qt::LeftButton);
1877 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
1878 q->setFlag(QQuickItem::ItemHasContents);
1879 #ifndef QT_NO_CLIPBOARD
1880 q->connect(q, SIGNAL(readOnlyChanged(bool)),
1881 q, SLOT(q_canPasteChanged()));
1882 q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()),
1883 q, SLOT(q_canPasteChanged()));
1884 canPaste = !m_readOnly && QGuiApplication::clipboard()->text().length() != 0;
1885 #endif // QT_NO_CLIPBOARD
1886 updateDisplayText();
1888 imHints &= ~Qt::ImhMultiLine;
1889 oldValidity = hasAcceptableInput(m_text);
1890 lastSelectionStart = 0;
1891 lastSelectionEnd = 0;
1892 selectedTextColor = m_palette.color(QPalette::HighlightedText);
1893 selectionColor = m_palette.color(QPalette::Highlight);
1894 determineHorizontalAlignment();
1896 if (!qmlDisableDistanceField()) {
1897 QTextOption option = m_textLayout.textOption();
1898 option.setUseDesignMetrics(true);
1899 m_textLayout.setTextOption(option);
1903 void QQuickTextInput::updateCursorRectangle()
1905 Q_D(QQuickTextInput);
1906 d->determineHorizontalAlignment();
1907 d->updateHorizontalScroll();
1908 updateRect();//TODO: Only update rect between pos's
1910 emit cursorRectangleChanged();
1912 d->cursorItem->setX(d->cursorToX() - d->hscroll);
1915 void QQuickTextInput::selectionChanged()
1917 Q_D(QQuickTextInput);
1918 updateRect();//TODO: Only update rect in selection
1919 emit selectedTextChanged();
1921 if (d->lastSelectionStart != d->selectionStart()) {
1922 d->lastSelectionStart = d->selectionStart();
1923 if (d->lastSelectionStart == -1)
1924 d->lastSelectionStart = d->m_cursor;
1925 emit selectionStartChanged();
1927 if (d->lastSelectionEnd != d->selectionEnd()) {
1928 d->lastSelectionEnd = d->selectionEnd();
1929 if (d->lastSelectionEnd == -1)
1930 d->lastSelectionEnd = d->m_cursor;
1931 emit selectionEndChanged();
1935 void QQuickTextInput::q_textChanged()
1937 Q_D(QQuickTextInput);
1939 emit displayTextChanged();
1941 d->determineHorizontalAlignment();
1942 d->updateHorizontalScroll();
1944 if (hasAcceptableInput() != d->oldValidity) {
1945 d->oldValidity = hasAcceptableInput();
1946 emit acceptableInputChanged();
1950 void QQuickTextInputPrivate::showCursor()
1952 if (textNode != 0 && textNode->cursorNode() != 0)
1953 textNode->cursorNode()->setColor(color);
1956 void QQuickTextInputPrivate::hideCursor()
1958 if (textNode != 0 && textNode->cursorNode() != 0)
1959 textNode->cursorNode()->setColor(QColor(0, 0, 0, 0));
1962 void QQuickTextInput::updateRect(const QRect &r)
1964 Q_D(QQuickTextInput);
1965 if (!isComponentComplete())
1969 d->textLayoutDirty = true;
1975 QRectF QQuickTextInput::boundingRect() const
1977 Q_D(const QQuickTextInput);
1978 QRectF r = QQuickImplicitSizeItem::boundingRect();
1980 int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->m_cursorWidth;
1982 // Could include font max left/right bearings to either side of rectangle.
1984 r.setRight(r.right() + cursorWidth);
1988 void QQuickTextInput::updateSize(bool needsRedraw)
1990 Q_D(QQuickTextInput);
1993 setImplicitSize(d->calculateTextWidth(), d->calculateTextHeight());
1994 if (w==width() && h==height() && needsRedraw)
1998 void QQuickTextInput::q_canPasteChanged()
2000 Q_D(QQuickTextInput);
2001 bool old = d->canPaste;
2002 #ifndef QT_NO_CLIPBOARD
2003 d->canPaste = !d->m_readOnly && QGuiApplication::clipboard()->text().length() != 0;
2005 if (d->canPaste != old)
2006 emit canPasteChanged();
2009 // ### these should come from QStyleHints
2010 const int textCursorWidth = 1;
2011 const bool fullWidthSelection = true;
2016 Updates the display text based of the current edit text
2017 If the text has changed will emit displayTextChanged()
2019 void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
2021 QString orig = m_textLayout.text();
2023 if (m_echoMode == QQuickTextInput::NoEcho)
2024 str = QString::fromLatin1("");
2028 if (m_echoMode == QQuickTextInput::Password
2029 || (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing))
2030 str.fill(m_passwordCharacter);
2032 // replace certain non-printable characters with spaces (to avoid
2033 // drawing boxes when using fonts that don't have glyphs for such
2035 QChar* uc = str.data();
2036 for (int i = 0; i < (int)str.length(); ++i) {
2037 if ((uc[i] < 0x20 && uc[i] != 0x09)
2038 || uc[i] == QChar::LineSeparator
2039 || uc[i] == QChar::ParagraphSeparator
2040 || uc[i] == QChar::ObjectReplacementCharacter)
2041 uc[i] = QChar(0x0020);
2044 m_textLayout.setText(str);
2046 QTextOption option = m_textLayout.textOption();
2047 option.setTextDirection(m_layoutDirection);
2048 option.setFlags(QTextOption::IncludeTrailingSpaces);
2049 m_textLayout.setTextOption(option);
2051 m_textLayout.beginLayout();
2052 QTextLine l = m_textLayout.createLine();
2053 m_textLayout.endLayout();
2054 m_ascent = qRound(l.ascent());
2056 if (str != orig || forceUpdate)
2057 emit q_func()->displayTextChanged();
2060 #ifndef QT_NO_CLIPBOARD
2064 Copies the currently selected text into the clipboard using the given
2067 \note If the echo mode is set to a mode other than Normal then copy
2068 will not work. This is to prevent using copy as a method of bypassing
2069 password features of the line control.
2071 void QQuickTextInputPrivate::copy(QClipboard::Mode mode) const
2073 QString t = selectedText();
2074 if (!t.isEmpty() && m_echoMode == QQuickTextInput::Normal) {
2075 QGuiApplication::clipboard()->setText(t, mode);
2082 Inserts the text stored in the application clipboard into the line
2087 void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode)
2089 QString clip = QGuiApplication::clipboard()->text(clipboardMode);
2090 if (!clip.isEmpty() || hasSelectedText()) {
2091 separate(); //make it a separate undo/redo command
2097 #endif // !QT_NO_CLIPBOARD
2102 Exits preedit mode and commits parts marked as tentative commit
2104 void QQuickTextInputPrivate::commitPreedit()
2109 qApp->inputPanel()->reset();
2111 if (!m_tentativeCommit.isEmpty()) {
2112 internalInsert(m_tentativeCommit);
2113 m_tentativeCommit.clear();
2114 finishChange(-1, true/*not used, not documented*/, false);
2117 m_preeditCursor = 0;
2118 m_textLayout.setPreeditArea(-1, QString());
2119 m_textLayout.clearAdditionalFormats();
2120 updateDisplayText(/*force*/ true);
2126 Handles the behavior for the backspace key or function.
2127 Removes the current selection if there is a selection, otherwise
2128 removes the character prior to the cursor position.
2132 void QQuickTextInputPrivate::backspace()
2134 int priorState = m_undoState;
2135 if (hasSelectedText()) {
2136 removeSelectedText();
2137 } else if (m_cursor) {
2140 m_cursor = prevMaskBlank(m_cursor);
2141 QChar uc = m_text.at(m_cursor);
2142 if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
2143 // second half of a surrogate, check if we have the first half as well,
2144 // if yes delete both at once
2145 uc = m_text.at(m_cursor - 1);
2146 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
2147 internalDelete(true);
2151 internalDelete(true);
2153 finishChange(priorState);
2159 Handles the behavior for the delete key or function.
2160 Removes the current selection if there is a selection, otherwise
2161 removes the character after the cursor position.
2165 void QQuickTextInputPrivate::del()
2167 int priorState = m_undoState;
2168 if (hasSelectedText()) {
2169 removeSelectedText();
2171 int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
2175 finishChange(priorState);
2181 Inserts the given \a newText at the current cursor position.
2182 If there is any selected text it is removed prior to insertion of
2185 void QQuickTextInputPrivate::insert(const QString &newText)
2187 int priorState = m_undoState;
2188 removeSelectedText();
2189 internalInsert(newText);
2190 finishChange(priorState);
2196 Clears the line control text.
2198 void QQuickTextInputPrivate::clear()
2200 int priorState = m_undoState;
2202 m_selend = m_text.length();
2203 removeSelectedText();
2205 finishChange(priorState, /*update*/false, /*edited*/false);
2211 Sets \a length characters from the given \a start position as selected.
2212 The given \a start position must be within the current text for
2213 the line control. If \a length characters cannot be selected, then
2214 the selection will extend to the end of the current text.
2216 void QQuickTextInputPrivate::setSelection(int start, int length)
2218 Q_Q(QQuickTextInput);
2221 if (start < 0 || start > (int)m_text.length()){
2222 qWarning("QQuickTextInputPrivate::setSelection: Invalid start position");
2227 if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
2230 m_selend = qMin(start + length, (int)m_text.length());
2231 m_cursor = m_selend;
2232 } else if (length < 0){
2233 if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
2235 m_selstart = qMax(start + length, 0);
2237 m_cursor = m_selstart;
2238 } else if (m_selstart != m_selend) {
2244 emitCursorPositionChanged();
2247 emit q->selectionChanged();
2248 emitCursorPositionChanged();
2254 Initializes the line control with a starting text value of \a txt.
2256 void QQuickTextInputPrivate::init(const QString &txt)
2260 updateDisplayText();
2261 m_cursor = m_text.length();
2267 Sets the password echo editing to \a editing. If password echo editing
2268 is true, then the text of the password is displayed even if the echo
2269 mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing
2270 does not affect other echo modes.
2272 void QQuickTextInputPrivate::updatePasswordEchoEditing(bool editing)
2274 m_passwordEchoEditing = editing;
2275 updateDisplayText();
2281 Returns the cursor position of the given \a x pixel value in relation
2282 to the displayed text. The given \a betweenOrOn specified what kind
2283 of cursor position is requested.
2285 int QQuickTextInputPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
2287 Q_Q(const QQuickTextInput);
2288 QRect cr = q->boundingRect().toRect();
2289 x-= cr.x() - hscroll;
2290 return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn);
2296 Fixes the current text so that it is valid given any set validators.
2298 Returns true if the text was changed. Otherwise returns false.
2300 bool QQuickTextInputPrivate::fixup() // this function assumes that validate currently returns != Acceptable
2302 #ifndef QT_NO_VALIDATOR
2304 QString textCopy = m_text;
2305 int cursorCopy = m_cursor;
2306 m_validator->fixup(textCopy);
2307 if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
2308 if (textCopy != m_text || cursorCopy != m_cursor)
2309 internalSetText(textCopy, cursorCopy);
2320 Moves the cursor to the given position \a pos. If \a mark is true will
2321 adjust the currently selected text.
2323 void QQuickTextInputPrivate::moveCursor(int pos, bool mark)
2325 Q_Q(QQuickTextInput);
2328 if (pos != m_cursor) {
2331 pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
2335 if (m_selend > m_selstart && m_cursor == m_selstart)
2337 else if (m_selend > m_selstart && m_cursor == m_selend)
2338 anchor = m_selstart;
2341 m_selstart = qMin(anchor, pos);
2342 m_selend = qMax(anchor, pos);
2343 updateDisplayText();
2348 if (mark || m_selDirty) {
2350 emit q->selectionChanged();
2352 emitCursorPositionChanged();
2358 Applies the given input method event \a event to the text of the line
2361 void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
2363 Q_Q(QQuickTextInput);
2365 int priorState = -1;
2366 bool isGettingInput = !event->commitString().isEmpty()
2367 || event->preeditString() != preeditAreaText()
2368 || event->replacementLength() > 0;
2369 bool cursorPositionChanged = false;
2370 bool selectionChange = false;
2372 if (isGettingInput) {
2373 // If any text is being input, remove selected text.
2374 priorState = m_undoState;
2375 if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
2376 updatePasswordEchoEditing(true);
2378 m_selend = m_text.length();
2380 removeSelectedText();
2383 int c = m_cursor; // cursor position after insertion of commit string
2384 if (event->replacementStart() <= 0)
2385 c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength());
2387 m_cursor += event->replacementStart();
2391 // insert commit string
2392 if (event->replacementLength()) {
2393 m_selstart = m_cursor;
2394 m_selend = m_selstart + event->replacementLength();
2395 removeSelectedText();
2397 if (!event->commitString().isEmpty()) {
2398 internalInsert(event->commitString());
2399 cursorPositionChanged = true;
2402 m_cursor = qBound(0, c, m_text.length());
2404 for (int i = 0; i < event->attributes().size(); ++i) {
2405 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
2406 if (a.type == QInputMethodEvent::Selection) {
2407 m_cursor = qBound(0, a.start + a.length, m_text.length());
2409 m_selstart = qMax(0, qMin(a.start, m_text.length()));
2410 m_selend = m_cursor;
2411 if (m_selend < m_selstart) {
2412 qSwap(m_selstart, m_selend);
2414 selectionChange = true;
2416 m_selstart = m_selend = 0;
2418 cursorPositionChanged = true;
2422 m_textLayout.setPreeditArea(m_cursor, event->preeditString());
2424 const int oldPreeditCursor = m_preeditCursor;
2425 m_preeditCursor = event->preeditString().length();
2426 m_hideCursor = false;
2427 QList<QTextLayout::FormatRange> formats;
2428 for (int i = 0; i < event->attributes().size(); ++i) {
2429 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
2430 if (a.type == QInputMethodEvent::Cursor) {
2431 m_preeditCursor = a.start;
2432 m_hideCursor = !a.length;
2433 } else if (a.type == QInputMethodEvent::TextFormat) {
2434 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
2436 QTextLayout::FormatRange o;
2437 o.start = a.start + m_cursor;
2438 o.length = a.length;
2444 m_textLayout.setAdditionalFormats(formats);
2445 updateDisplayText(/*force*/ true);
2446 if (cursorPositionChanged)
2447 emitCursorPositionChanged();
2448 else if (m_preeditCursor != oldPreeditCursor)
2449 q->updateCursorRectangle();
2451 bool tentativeCommitChanged = m_tentativeCommit != event->tentativeCommitString();
2453 if (tentativeCommitChanged) {
2455 m_tentativeCommit = event->tentativeCommitString();
2458 if (isGettingInput || tentativeCommitChanged)
2459 finishChange(priorState);
2461 if (selectionChange)
2462 emit q->selectionChanged();
2468 Sets the selection to cover the word at the given cursor position.
2469 The word boundaries are defined by the behavior of QTextLayout::SkipWords
2472 void QQuickTextInputPrivate::selectWordAtPos(int cursor)
2474 int next = cursor + 1;
2477 int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords);
2478 moveCursor(c, false);
2479 // ## text layout should support end of words.
2480 int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
2481 while (end > cursor && m_text[end-1].isSpace())
2483 moveCursor(end, true);
2489 Completes a change to the line control text. If the change is not valid
2490 will undo the line control state back to the given \a validateFromState.
2492 If \a edited is true and the change is valid, will emit textEdited() in
2493 addition to textChanged(). Otherwise only emits textChanged() on a valid
2496 The \a update value is currently unused.
2498 bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bool /*edited*/)
2500 Q_Q(QQuickTextInput);
2506 bool wasValidInput = m_validInput;
2507 m_validInput = true;
2508 #ifndef QT_NO_VALIDATOR
2510 QString textCopy = m_text;
2511 int cursorCopy = m_cursor;
2512 m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
2514 if (m_text != textCopy) {
2515 internalSetText(textCopy, cursorCopy);
2518 m_cursor = cursorCopy;
2520 if (!m_tentativeCommit.isEmpty()) {
2521 textCopy.insert(m_cursor, m_tentativeCommit);
2522 bool validInput = m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid;
2524 m_tentativeCommit.clear();
2527 m_tentativeCommit.clear();
2531 if (validateFromState >= 0 && wasValidInput && !m_validInput) {
2532 if (m_transactions.count())
2534 internalUndo(validateFromState);
2535 m_history.resize(m_undoState);
2536 if (m_modifiedState > m_undoState)
2537 m_modifiedState = -1;
2538 m_validInput = true;
2539 m_textDirty = false;
2541 updateDisplayText();
2544 m_textDirty = false;
2545 q_func()->q_textChanged();
2550 emit q->selectionChanged();
2552 emitCursorPositionChanged();
2559 An internal function for setting the text of the line control.
2561 void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool edited)
2563 Q_Q(QQuickTextInput);
2565 QString oldText = m_text;
2567 m_text = maskString(0, txt, true);
2568 m_text += clearString(m_text.length(), m_maxLength - m_text.length());
2570 m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
2573 m_modifiedState = m_undoState = 0;
2574 m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
2575 m_textDirty = (oldText != m_text);
2577 bool changed = finishChange(-1, true, edited);
2578 #ifdef QT_NO_ACCESSIBILITY
2582 QAccessible::updateAccessibility(q, 0, QAccessible::TextUpdated);
2590 Adds the given \a command to the undo history
2591 of the line control. Does not apply the command.
2593 void QQuickTextInputPrivate::addCommand(const Command &cmd)
2595 if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) {
2596 m_history.resize(m_undoState + 2);
2597 m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend);
2599 m_history.resize(m_undoState + 1);
2601 m_separator = false;
2602 m_history[m_undoState++] = cmd;
2608 Inserts the given string \a s into the line
2611 Also adds the appropriate commands into the undo history.
2612 This function does not call finishChange(), and may leave the text
2613 in an invalid state.
2615 void QQuickTextInputPrivate::internalInsert(const QString &s)
2617 if (hasSelectedText())
2618 addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
2620 QString ms = maskString(m_cursor, s);
2621 for (int i = 0; i < (int) ms.length(); ++i) {
2622 addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
2623 addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
2625 m_text.replace(m_cursor, ms.length(), ms);
2626 m_cursor += ms.length();
2627 m_cursor = nextMaskBlank(m_cursor);
2630 int remaining = m_maxLength - m_text.length();
2631 if (remaining != 0) {
2632 m_text.insert(m_cursor, s.left(remaining));
2633 for (int i = 0; i < (int) s.left(remaining).length(); ++i)
2634 addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
2643 deletes a single character from the current text. If \a wasBackspace,
2644 the character prior to the cursor is removed. Otherwise the character
2645 after the cursor is removed.
2647 Also adds the appropriate commands into the undo history.
2648 This function does not call finishChange(), and may leave the text
2649 in an invalid state.
2651 void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
2653 if (m_cursor < (int) m_text.length()) {
2654 if (hasSelectedText())
2655 addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
2656 addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
2657 m_cursor, m_text.at(m_cursor), -1, -1));
2659 m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
2660 addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
2662 m_text.remove(m_cursor, 1);
2671 removes the currently selected text from the line control.
2673 Also adds the appropriate commands into the undo history.
2674 This function does not call finishChange(), and may leave the text
2675 in an invalid state.
2677 void QQuickTextInputPrivate::removeSelectedText()
2679 if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
2682 addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
2683 if (m_selstart <= m_cursor && m_cursor < m_selend) {
2684 // cursor is within the selection. Split up the commands
2685 // to be able to restore the correct cursor position
2686 for (i = m_cursor; i >= m_selstart; --i)
2687 addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
2688 for (i = m_selend - 1; i > m_cursor; --i)
2689 addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
2691 for (i = m_selend-1; i >= m_selstart; --i)
2692 addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
2695 m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
2696 for (int i = 0; i < m_selend - m_selstart; ++i)
2697 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
2699 m_text.remove(m_selstart, m_selend - m_selstart);
2701 if (m_cursor > m_selstart)
2702 m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
2711 Parses the input mask specified by \a maskFields to generate
2712 the mask data used to handle input masks.
2714 void QQuickTextInputPrivate::parseInputMask(const QString &maskFields)
2716 int delimiter = maskFields.indexOf(QLatin1Char(';'));
2717 if (maskFields.isEmpty() || delimiter == 0) {
2719 delete [] m_maskData;
2721 m_maxLength = 32767;
2722 internalSetText(QString());
2727 if (delimiter == -1) {
2728 m_blank = QLatin1Char(' ');
2729 m_inputMask = maskFields;
2731 m_inputMask = maskFields.left(delimiter);
2732 m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
2735 // calculate m_maxLength / m_maskData length
2738 for (int i=0; i<m_inputMask.length(); i++) {
2739 c = m_inputMask.at(i);
2740 if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) {
2744 if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
2745 c != QLatin1Char('<') && c != QLatin1Char('>') &&
2746 c != QLatin1Char('{') && c != QLatin1Char('}') &&
2747 c != QLatin1Char('[') && c != QLatin1Char(']'))
2751 delete [] m_maskData;
2752 m_maskData = new MaskInputData[m_maxLength];
2754 MaskInputData::Casemode m = MaskInputData::NoCaseMode;
2757 bool escape = false;
2759 for (int i = 0; i < m_inputMask.length(); i++) {
2760 c = m_inputMask.at(i);
2763 m_maskData[index].maskChar = c;
2764 m_maskData[index].separator = s;
2765 m_maskData[index].caseMode = m;
2768 } else if (c == QLatin1Char('<')) {
2769 m = MaskInputData::Lower;
2770 } else if (c == QLatin1Char('>')) {
2771 m = MaskInputData::Upper;
2772 } else if (c == QLatin1Char('!')) {
2773 m = MaskInputData::NoCaseMode;
2774 } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
2775 switch (c.unicode()) {
2801 m_maskData[index].maskChar = c;
2802 m_maskData[index].separator = s;
2803 m_maskData[index].caseMode = m;
2808 internalSetText(m_text);
2815 checks if the key is valid compared to the inputMask
2817 bool QQuickTextInputPrivate::isValidInput(QChar key, QChar mask) const
2819 switch (mask.unicode()) {
2825 if (key.isLetter() || key == m_blank)
2829 if (key.isLetterOrNumber())
2833 if (key.isLetterOrNumber() || key == m_blank)
2841 if (key.isPrint() || key == m_blank)
2849 if (key.isNumber() || key == m_blank)
2853 if (key.isNumber() && key.digitValue() > 0)
2857 if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
2861 if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
2865 if (key == QLatin1Char('0') || key == QLatin1Char('1'))
2869 if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
2873 if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
2877 if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
2889 Returns true if the given text \a str is valid for any
2890 validator or input mask set for the line control.
2892 Otherwise returns false
2894 bool QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
2896 #ifndef QT_NO_VALIDATOR
2897 QString textCopy = str;
2898 int cursorCopy = m_cursor;
2899 if (m_validator && m_validator->validate(textCopy, cursorCopy)
2900 != QValidator::Acceptable)
2907 if (str.length() != m_maxLength)
2910 for (int i=0; i < m_maxLength; ++i) {
2911 if (m_maskData[i].separator) {
2912 if (str.at(i) != m_maskData[i].maskChar)
2915 if (!isValidInput(str.at(i), m_maskData[i].maskChar))
2925 Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
2926 specifies from where characters should be gotten when a separator is met in \a str - true means
2927 that blanks will be used, false that previous input is used.
2928 Calling this when no inputMask is set is undefined.
2930 QString QQuickTextInputPrivate::maskString(uint pos, const QString &str, bool clear) const
2932 if (pos >= (uint)m_maxLength)
2933 return QString::fromLatin1("");
2936 fill = clear ? clearString(0, m_maxLength) : m_text;
2939 QString s = QString::fromLatin1("");
2941 while (i < m_maxLength) {
2942 if (strIndex < str.length()) {
2943 if (m_maskData[i].separator) {
2944 s += m_maskData[i].maskChar;
2945 if (str[(int)strIndex] == m_maskData[i].maskChar)
2949 if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) {
2950 switch (m_maskData[i].caseMode) {
2951 case MaskInputData::Upper:
2952 s += str[(int)strIndex].toUpper();
2954 case MaskInputData::Lower:
2955 s += str[(int)strIndex].toLower();
2958 s += str[(int)strIndex];
2962 // search for separator first
2963 int n = findInMask(i, true, true, str[(int)strIndex]);
2965 if (str.length() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[(int)strIndex]))) {
2966 s += fill.mid(i, n-i+1);
2967 i = n + 1; // update i to find + 1
2970 // search for valid m_blank if not
2971 n = findInMask(i, true, false, str[(int)strIndex]);
2973 s += fill.mid(i, n-i);
2974 switch (m_maskData[n].caseMode) {
2975 case MaskInputData::Upper:
2976 s += str[(int)strIndex].toUpper();
2978 case MaskInputData::Lower:
2979 s += str[(int)strIndex].toLower();
2982 s += str[(int)strIndex];
2984 i = n + 1; // updates i to find + 1
3002 Returns a "cleared" string with only separators and blank chars.
3003 Calling this when no inputMask is set is undefined.
3005 QString QQuickTextInputPrivate::clearString(uint pos, uint len) const
3007 if (pos >= (uint)m_maxLength)
3011 int end = qMin((uint)m_maxLength, pos + len);
3012 for (int i = pos; i < end; ++i)
3013 if (m_maskData[i].separator)
3014 s += m_maskData[i].maskChar;
3024 Strips blank parts of the input in a QQuickTextInputPrivate when an inputMask is set,
3025 separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
3027 QString QQuickTextInputPrivate::stripString(const QString &str) const
3033 int end = qMin(m_maxLength, (int)str.length());
3034 for (int i = 0; i < end; ++i)
3035 if (m_maskData[i].separator)
3036 s += m_maskData[i].maskChar;
3038 if (str[i] != m_blank)
3046 searches forward/backward in m_maskData for either a separator or a m_blank
3048 int QQuickTextInputPrivate::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
3050 if (pos >= m_maxLength || pos < 0)
3053 int end = forward ? m_maxLength : -1;
3054 int step = forward ? 1 : -1;
3058 if (findSeparator) {
3059 if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
3062 if (!m_maskData[i].separator) {
3063 if (searchChar.isNull())
3065 else if (isValidInput(searchChar, m_maskData[i].maskChar))
3074 void QQuickTextInputPrivate::internalUndo(int until)
3076 if (!isUndoAvailable())
3079 while (m_undoState && m_undoState > until) {
3080 Command& cmd = m_history[--m_undoState];
3083 m_text.remove(cmd.pos, 1);
3087 m_selstart = cmd.selStart;
3088 m_selend = cmd.selEnd;
3092 case RemoveSelection:
3093 m_text.insert(cmd.pos, cmd.uc);
3094 m_cursor = cmd.pos + 1;
3097 case DeleteSelection:
3098 m_text.insert(cmd.pos, cmd.uc);
3104 if (until < 0 && m_undoState) {
3105 Command& next = m_history[m_undoState-1];
3106 if (next.type != cmd.type && next.type < RemoveSelection
3107 && (cmd.type < RemoveSelection || next.type == Separator))
3112 emitCursorPositionChanged();
3115 void QQuickTextInputPrivate::internalRedo()
3117 if (!isRedoAvailable())
3120 while (m_undoState < (int)m_history.size()) {
3121 Command& cmd = m_history[m_undoState++];
3124 m_text.insert(cmd.pos, cmd.uc);
3125 m_cursor = cmd.pos + 1;
3128 m_selstart = cmd.selStart;
3129 m_selend = cmd.selEnd;
3134 case RemoveSelection:
3135 case DeleteSelection:
3136 m_text.remove(cmd.pos, 1);
3137 m_selstart = cmd.selStart;
3138 m_selend = cmd.selEnd;
3142 m_selstart = cmd.selStart;
3143 m_selend = cmd.selEnd;
3147 if (m_undoState < (int)m_history.size()) {
3148 Command& next = m_history[m_undoState];
3149 if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
3150 && (next.type < RemoveSelection || cmd.type == Separator))
3155 emitCursorPositionChanged();
3161 If the current cursor position differs from the last emitted cursor
3162 position, emits cursorPositionChanged().
3164 void QQuickTextInputPrivate::emitCursorPositionChanged()
3166 Q_Q(QQuickTextInput);
3167 if (m_cursor != m_lastCursorPos) {
3168 m_lastCursorPos = m_cursor;
3170 q->updateCursorRectangle();
3171 emit q->cursorPositionChanged();
3172 // XXX todo - not in 4.8?
3174 resetCursorBlinkTimer();
3177 if (!hasSelectedText()) {
3178 if (lastSelectionStart != m_cursor) {
3179 lastSelectionStart = m_cursor;
3180 emit q->selectionStartChanged();
3182 if (lastSelectionEnd != m_cursor) {
3183 lastSelectionEnd = m_cursor;
3184 emit q->selectionEndChanged();
3188 #ifndef QT_NO_ACCESSIBILITY
3189 QAccessible::updateAccessibility(q, 0, QAccessible::TextCaretMoved);
3195 void QQuickTextInputPrivate::setCursorBlinkPeriod(int msec)
3197 Q_Q(QQuickTextInput);
3198 if (msec == m_blinkPeriod)
3201 q->killTimer(m_blinkTimer);
3204 m_blinkTimer = q->startTimer(msec / 2);
3208 if (m_blinkStatus == 1)
3209 emit q->updateRect(inputMask().isEmpty() ? q->cursorRectangle() : QRect());
3211 m_blinkPeriod = msec;
3214 void QQuickTextInputPrivate::resetCursorBlinkTimer()
3216 Q_Q(QQuickTextInput);
3217 if (m_blinkPeriod == 0 || m_blinkTimer == 0)
3219 q->killTimer(m_blinkTimer);
3220 m_blinkTimer = q->startTimer(m_blinkPeriod / 2);
3224 void QQuickTextInput::timerEvent(QTimerEvent *event)
3226 Q_D(QQuickTextInput);
3227 if (event->timerId() == d->m_blinkTimer) {
3228 d->m_blinkStatus = !d->m_blinkStatus;
3229 updateRect(inputMask().isEmpty() ? cursorRectangle() : QRect());
3230 } else if (event->timerId() == d->m_deleteAllTimer) {
3231 killTimer(d->m_deleteAllTimer);
3232 d->m_deleteAllTimer = 0;
3237 void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
3239 Q_Q(QQuickTextInput);
3240 bool inlineCompletionAccepted = false;
3242 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
3243 if (hasAcceptableInput(m_text) || fixup()) {
3246 if (inlineCompletionAccepted)
3253 if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit
3254 && !m_passwordEchoEditing
3256 && !event->text().isEmpty()
3257 && !(event->modifiers() & Qt::ControlModifier)) {
3258 // Clear the edit and reset to normal echo mode while editing; the
3259 // echo mode switches back when the edit loses focus
3260 // ### resets current content. dubious code; you can
3261 // navigate with keys up, down, back, and select(?), but if you press
3262 // "left" or "right" it clears?
3263 updatePasswordEchoEditing(true);
3267 bool unknown = false;
3268 bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
3272 #ifndef QT_NO_SHORTCUT
3273 else if (event == QKeySequence::Undo) {
3277 else if (event == QKeySequence::Redo) {
3281 else if (event == QKeySequence::SelectAll) {
3284 #ifndef QT_NO_CLIPBOARD
3285 else if (event == QKeySequence::Copy) {
3288 else if (event == QKeySequence::Paste) {
3290 QClipboard::Mode mode = QClipboard::Clipboard;
3294 else if (event == QKeySequence::Cut) {
3300 else if (event == QKeySequence::DeleteEndOfLine) {
3302 setSelection(m_cursor, end());
3307 #endif //QT_NO_CLIPBOARD
3308 else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
3311 else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
3314 else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
3317 else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
3320 else if (event == QKeySequence::MoveToNextChar) {
3321 if (hasSelectedText()) {
3322 moveCursor(selectionEnd(), false);
3324 cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
3327 else if (event == QKeySequence::SelectNextChar) {
3328 cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
3330 else if (event == QKeySequence::MoveToPreviousChar) {
3331 if (hasSelectedText()) {
3332 moveCursor(selectionStart(), false);
3334 cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
3337 else if (event == QKeySequence::SelectPreviousChar) {
3338 cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
3340 else if (event == QKeySequence::MoveToNextWord) {
3341 if (m_echoMode == QQuickTextInput::Normal)
3342 layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
3344 layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
3346 else if (event == QKeySequence::MoveToPreviousWord) {
3347 if (m_echoMode == QQuickTextInput::Normal)
3348 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
3349 else if (!m_readOnly) {
3350 layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
3353 else if (event == QKeySequence::SelectNextWord) {
3354 if (m_echoMode == QQuickTextInput::Normal)
3355 layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
3357 layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
3359 else if (event == QKeySequence::SelectPreviousWord) {
3360 if (m_echoMode == QQuickTextInput::Normal)
3361 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
3363 layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
3365 else if (event == QKeySequence::Delete) {
3369 else if (event == QKeySequence::DeleteEndOfWord) {
3371 cursorWordForward(true);
3375 else if (event == QKeySequence::DeleteStartOfWord) {
3377 cursorWordBackward(true);
3381 #endif // QT_NO_SHORTCUT
3383 bool handled = false;
3384 if (event->modifiers() & Qt::ControlModifier) {
3385 switch (event->key()) {
3386 case Qt::Key_Backspace:
3388 cursorWordBackward(true);
3396 } else { // ### check for *no* modifier
3397 switch (event->key()) {
3398 case Qt::Key_Backspace:
3410 if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
3411 setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
3415 if (unknown && !m_readOnly) {
3416 QString t = event->text();
3417 if (!t.isEmpty() && t.at(0).isPrint()) {