Fix QQuickTextInput::moveCursorSelection()
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextinput.cpp
index e514c38..22551c9 100644 (file)
@@ -1,38 +1,38 @@
 /****************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
 **
-** This file is part of the QtDeclarative module of the Qt Toolkit.
+** This file is part of the QtQml module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
 ** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
 ** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
 **
 **
 ** $QT_END_LICENSE$
 
 #include "qquicktextinput_p.h"
 #include "qquicktextinput_p_p.h"
-#include "qquickcanvas.h"
+#include "qquickwindow.h"
+#include "qquicktextutil_p.h"
+
+#include <private/qqmlglobal_p.h>
 
-#include <private/qdeclarativeglobal_p.h>
 
-#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtQml/qqmlinfo.h>
 #include <QtGui/qevent.h>
 #include <QTextBoundaryFinder>
 #include "qquicktextnode_p.h"
 #include <QtQuick/qsgsimplerectnode.h>
 
 #include <QtGui/qstylehints.h>
-#include <QtGui/qinputpanel.h>
+#include <QtGui/qinputmethod.h>
 
 #ifndef QT_NO_ACCESSIBILITY
 #include "qaccessible.h"
@@ -62,18 +65,16 @@ QT_BEGIN_NAMESPACE
 
 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
 
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
-static const int qt_passwordEchoDelay = QT_GUI_PASSWORD_ECHO_DELAY;
-#endif
-
 /*!
-    \qmlclass TextInput QQuickTextInput
+    \qmltype TextInput
+    \instantiates QQuickTextInput
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
-    \brief The TextInput item displays an editable line of text.
+    \ingroup qtquick-visual
+    \ingroup qtquick-input
     \inherits Item
+    \brief Displays an editable line of text
 
-    The TextInput element displays a single line of editable plain text.
+    The TextInput type displays a single line of editable plain text.
 
     TextInput is used to accept a line of text input. Input constraints
     can be placed on a TextInput item (for example, through a \l validator or \l inputMask),
@@ -102,10 +103,11 @@ void QQuickTextInput::componentComplete()
 
     QQuickImplicitSizeItem::componentComplete();
 
+    d->checkIsValid();
     d->updateLayout();
     updateCursorRectangle();
-    if (d->cursorComponent && d->cursorComponent->isReady())
-        createCursor();
+    if (d->cursorComponent && isCursorVisible())
+        QQuickTextUtil::createCursor(d);
 }
 
 /*!
@@ -118,8 +120,6 @@ QString QQuickTextInput::text() const
     Q_D(const QQuickTextInput);
 
     QString content = d->m_text;
-    if (!d->m_tentativeCommit.isEmpty())
-        content.insert(d->m_cursor, d->m_tentativeCommit);
     QString res = d->m_maskData ? d->stripString(content) : content;
     return (res.isNull() ? QString::fromLatin1("") : res);
 }
@@ -129,12 +129,83 @@ void QQuickTextInput::setText(const QString &s)
     Q_D(QQuickTextInput);
     if (s == text())
         return;
-    if (d->composeMode())
-        qApp->inputPanel()->reset();
-    d->m_tentativeCommit.clear();
+
+    d->cancelPreedit();
     d->internalSetText(s, -1, false);
 }
 
+
+/*!
+    \qmlproperty enumeration QtQuick2::TextInput::renderType
+
+    Override the default rendering type for this component.
+
+    Supported render types are:
+    \list
+    \li Text.QtRendering - the default
+    \li Text.NativeRendering
+    \endlist
+
+    Select Text.NativeRendering if you prefer text to look native on the target platform and do
+    not require advanced features such as transformation of the text. Using such features in
+    combination with the NativeRendering render type will lend poor and sometimes pixelated
+    results.
+*/
+QQuickTextInput::RenderType QQuickTextInput::renderType() const
+{
+    Q_D(const QQuickTextInput);
+    return d->renderType;
+}
+
+void QQuickTextInput::setRenderType(QQuickTextInput::RenderType renderType)
+{
+    Q_D(QQuickTextInput);
+    if (d->renderType == renderType)
+        return;
+
+    d->renderType = renderType;
+    emit renderTypeChanged();
+
+    if (isComponentComplete())
+        d->updateLayout();
+}
+
+/*!
+    \qmlproperty int QtQuick2::TextInput::length
+
+    Returns the total number of characters in the TextInput item.
+
+    If the TextInput has an inputMask the length will include mask characters and may differ
+    from the length of the string returned by the \l text property.
+
+    This property can be faster than querying the length the \l text property as it doesn't
+    require any copying or conversion of the TextInput's internal string data.
+*/
+
+int QQuickTextInput::length() const
+{
+    Q_D(const QQuickTextInput);
+    return d->m_text.length();
+}
+
+/*!
+    \qmlmethod string QtQuick2::TextInput::getText(int start, int end)
+
+    Returns the section of text that is between the \a start and \a end positions.
+
+    If the TextInput has an inputMask the length will include mask characters.
+*/
+
+QString QQuickTextInput::getText(int start, int end) const
+{
+    Q_D(const QQuickTextInput);
+
+    if (start > end)
+        qSwap(start, end);
+
+    return d->m_text.mid(start, end - start);
+}
+
 QString QQuickTextInputPrivate::realText() const
 {
     QString res = m_maskData ? stripString(m_text) : m_text;
@@ -164,11 +235,11 @@ QString QQuickTextInputPrivate::realText() const
 
     The weight can be one of:
     \list
-    \o Font.Light
-    \o Font.Normal - the default
-    \o Font.DemiBold
-    \o Font.Bold
-    \o Font.Black
+    \li Font.Light
+    \li Font.Normal - the default
+    \li Font.DemiBold
+    \li Font.Bold
+    \li Font.Black
     \endlist
 
     \qml
@@ -234,11 +305,11 @@ QString QQuickTextInputPrivate::realText() const
     Sets the capitalization for the text.
 
     \list
-    \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
-    \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
-    \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
-    \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
-    \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
+    \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
+    \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
+    \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
+    \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
+    \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
     \endlist
 
     \qml
@@ -269,6 +340,7 @@ void QQuickTextInput::setFont(const QFont &font)
     if (oldFont != d->font) {
         d->updateLayout();
         updateCursorRectangle();
+        updateInputMethod(Qt::ImCursorRectangle | Qt::ImFont);
     }
     emit fontChanged(d->sourceFont);
 }
@@ -290,8 +362,9 @@ void QQuickTextInput::setColor(const QColor &c)
     if (c != d->color) {
         d->color = c;
         d->textLayoutDirty = true;
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
-        emit colorChanged(c);
+        emit colorChanged();
     }
 }
 
@@ -314,12 +387,12 @@ void QQuickTextInput::setSelectionColor(const QColor &color)
         return;
 
     d->selectionColor = color;
-    d->m_palette.setColor(QPalette::Highlight, d->selectionColor);
     if (d->hasSelectedText()) {
         d->textLayoutDirty = true;
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
     }
-    emit selectionColorChanged(color);
+    emit selectionColorChanged();
 }
 /*!
     \qmlproperty color QtQuick2::TextInput::selectedTextColor
@@ -339,12 +412,12 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
         return;
 
     d->selectedTextColor = color;
-    d->m_palette.setColor(QPalette::HighlightedText, d->selectedTextColor);
     if (d->hasSelectedText()) {
         d->textLayoutDirty = true;
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
     }
-    emit selectedTextColorChanged(color);
+    emit selectedTextColorChanged();
 }
 
 /*!
@@ -366,8 +439,8 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
     The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and
     \c TextInput.AlignHCenter.
 
-    Valid values for \c verticalAlignment are \c TextEdit.AlignTop (default),
-    \c TextEdit.AlignBottom \c TextEdit.AlignVCenter.
+    Valid values for \c verticalAlignment are \c TextInput.AlignTop (default),
+    \c TextInput.AlignBottom \c TextInput.AlignVCenter.
 
     When using the attached property LayoutMirroring::enabled to mirror application
     layouts, the horizontal alignment of text will also be mirrored. However, the property
@@ -434,15 +507,48 @@ bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment alignment, bo
     return false;
 }
 
+Qt::LayoutDirection QQuickTextInputPrivate::textDirection() const
+{
+    QString text = m_text;
+    if (text.isEmpty())
+        text = m_textLayout.preeditAreaText();
+
+    const QChar *character = text.constData();
+    while (!character->isNull()) {
+        switch (character->direction()) {
+        case QChar::DirL:
+            return Qt::LeftToRight;
+        case QChar::DirR:
+        case QChar::DirAL:
+        case QChar::DirAN:
+            return Qt::RightToLeft;
+        default:
+            break;
+        }
+        character++;
+    }
+    return Qt::LayoutDirectionAuto;
+}
+
+Qt::LayoutDirection QQuickTextInputPrivate::layoutDirection() const
+{
+    Qt::LayoutDirection direction = m_layoutDirection;
+    if (direction == Qt::LayoutDirectionAuto) {
+        direction = textDirection();
+        if (direction == Qt::LayoutDirectionAuto)
+            direction = qApp->inputMethod()->inputDirection();
+    }
+    return (direction == Qt::LayoutDirectionAuto) ? Qt::LeftToRight : direction;
+}
+
 bool QQuickTextInputPrivate::determineHorizontalAlignment()
 {
     if (hAlignImplicit) {
         // if no explicit alignment has been set, follow the natural layout direction of the text
-        QString text = q_func()->text();
-        if (text.isEmpty())
-            text = m_textLayout.preeditAreaText();
-        bool isRightToLeft = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
-        return setHAlign(isRightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
+        Qt::LayoutDirection direction = textDirection();
+        if (direction == Qt::LayoutDirectionAuto)
+            direction = qApp->inputMethod()->inputDirection();
+        return setHAlign(direction == Qt::RightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
     }
     return false;
 }
@@ -468,14 +574,14 @@ void QQuickTextInput::setVAlign(QQuickTextInput::VAlignment alignment)
 /*!
     \qmlproperty enumeration QtQuick2::TextInput::wrapMode
 
-    Set this property to wrap the text to the TextEdit item's width.
+    Set this property to wrap the text to the TextInput item's width.
     The text will only wrap if an explicit width has been set.
 
     \list
-    \o TextInput.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
-    \o TextInput.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
-    \o TextInput.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
-    \o 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.
+    \li TextInput.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
+    \li TextInput.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
+    \li TextInput.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
+    \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.
     \endlist
 
     The default is TextInput.NoWrap. If you set a width, consider using TextInput.Wrap.
@@ -533,7 +639,9 @@ void QQuickTextInput::setReadOnly(bool ro)
     d->m_readOnly = ro;
     if (!ro)
         d->setCursorPosition(d->end());
-
+    updateInputMethod(Qt::ImEnabled);
+    q_canPasteChanged();
+    d->emitUndoRedoChanged();
     emit readOnlyChanged(ro);
 }
 
@@ -576,7 +684,7 @@ void QQuickTextInput::setMaxLength(int ml)
     forward keys to it and you desire it to look active when this happens
     (but without actually giving it active focus).
 
-    It should not be set directly on the element, like in the below QML,
+    It should not be set directly on the item, like in the below QML,
     as the specified value will be overridden an lost on focus changes.
 
     \code
@@ -601,8 +709,13 @@ void QQuickTextInput::setCursorVisible(bool on)
     if (d->cursorVisible == on)
         return;
     d->cursorVisible = on;
-    d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
-    update();
+    if (on && isComponentComplete())
+        QQuickTextUtil::createCursor(d);
+    if (!d->cursorItem) {
+        d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+        update();
+    }
     emit cursorVisibleChanged(d->cursorVisible);
 }
 
@@ -625,26 +738,26 @@ void QQuickTextInput::setCursorPosition(int cp)
 }
 
 /*!
-  Returns a Rect which encompasses the cursor, but which may be larger than is
-  required. Ignores custom cursor delegates.
+    \qmlproperty rectangle QtQuick2::TextInput::cursorRectangle
+
+    The rectangle where the standard text cursor is rendered within the text input.  Read only.
+
+    The position and height of a custom cursorDelegate are updated to follow the cursorRectangle
+    automatically when it changes.  The width of the delegate is unaffected by changes in the
+    cursor rectangle.
 */
-QRect QQuickTextInput::cursorRectangle() const
+
+QRectF QQuickTextInput::cursorRectangle() const
 {
     Q_D(const QQuickTextInput);
 
-    int c = d->m_cursor;
-    if (d->m_preeditCursor != -1)
-        c += d->m_preeditCursor;
+    int c = d->m_cursor + d->m_preeditCursor;
     if (d->m_echoMode == NoEcho)
         c = 0;
     QTextLine l = d->m_textLayout.lineForTextPosition(c);
     if (!l.isValid())
-        return QRect();
-    return QRect(
-            qRound(l.cursorToX(c) - d->hscroll),
-            qRound(l.y() - d->vscroll),
-            d->m_cursorWidth,
-            qCeil(l.height()));
+        return QRectF();
+    return QRectF(l.cursorToX(c) - d->hscroll, l.y() - d->vscroll, 1, l.height());
 }
 
 /*!
@@ -678,7 +791,7 @@ int QQuickTextInput::selectionEnd() const
     return d->lastSelectionEnd;
 }
 /*!
-    \qmlmethod void QtQuick2::TextInput::select(int start, int end)
+    \qmlmethod QtQuick2::TextInput::select(int start, int end)
 
     Causes the text from \a start to \a end to be selected.
 
@@ -693,7 +806,7 @@ int QQuickTextInput::selectionEnd() const
 void QQuickTextInput::select(int start, int end)
 {
     Q_D(QQuickTextInput);
-    if (start < 0 || end < 0 || start > text().length() || end > text().length())
+    if (start < 0 || end < 0 || start > d->m_text.length() || end > d->m_text.length())
         return;
     d->setSelection(start, end-start);
 }
@@ -767,17 +880,56 @@ void QQuickTextInput::setAutoScroll(bool b)
 #ifndef QT_NO_VALIDATOR
 
 /*!
-    \qmlclass IntValidator QIntValidator
+    \qmltype IntValidator
+    \instantiates QIntValidator
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
+    \ingroup qtquick-text-utility
+    \brief Defines a validator for integer values
 
-    This element provides a validator for integer values.
+    The IntValidator type provides a validator for integer values.
 
-    IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and
-    will accept locale specific digits, group separators, and positive and negative signs.  In
-    addition, IntValidator is always guaranteed to accept a number formatted according to the "C"
-    locale.
+    If no \l locale is set IntValidator uses the \l {QLocale::setDefault()}{default locale} to
+    interpret the number and will accept locale specific digits, group separators, and positive
+    and negative signs.  In addition, IntValidator is always guaranteed to accept a number
+    formatted according to the "C" locale.
 */
+
+
+QQuickIntValidator::QQuickIntValidator(QObject *parent)
+    : QIntValidator(parent)
+{
+}
+
+/*!
+    \qmlproperty string QtQuick2::IntValidator::locale
+
+    This property holds the name of the locale used to interpret the number.
+
+    \sa QML:Qt::locale()
+*/
+
+QString QQuickIntValidator::localeName() const
+{
+    return locale().name();
+}
+
+void QQuickIntValidator::setLocaleName(const QString &name)
+{
+    if (locale().name() != name) {
+        setLocale(QLocale(name));
+        emit localeNameChanged();
+    }
+}
+
+void QQuickIntValidator::resetLocaleName()
+{
+    QLocale defaultLocale;
+    if (locale() != defaultLocale) {
+        setLocale(defaultLocale);
+        emit localeNameChanged();
+    }
+}
+
 /*!
     \qmlproperty int QtQuick2::IntValidator::top
 
@@ -792,13 +944,67 @@ void QQuickTextInput::setAutoScroll(bool b)
 */
 
 /*!
-    \qmlclass DoubleValidator QDoubleValidator
+    \qmltype DoubleValidator
+    \instantiates QDoubleValidator
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
+    \ingroup qtquick-text-utility
+    \brief Defines a validator for non-integer numbers
+
+    The DoubleValidator type provides a validator for non-integer numbers.
+
+    Input is accepted if it contains a double that is within the valid range
+    and is in the  correct format.
+
+    Input is accepected but invalid if it contains a double that is outside
+    the range or is in the wrong format; e.g. with too many digits after the
+    decimal point or is empty.
+
+    Input is rejected if it is not a double.
+
+    Note: If the valid range consists of just positive doubles (e.g. 0.0 to
+    100.0) and input is a negative double then it is rejected. If \l notation
+    is set to DoubleValidator.StandardNotation, and  the input contains more
+    digits before the decimal point than a double in the valid range may have,
+    it is also rejected. If \l notation is DoubleValidator.ScientificNotation,
+    and the input is not in the valid range, it is accecpted but invalid. The
+    value may yet become valid by changing the exponent.
+*/
+
+QQuickDoubleValidator::QQuickDoubleValidator(QObject *parent)
+    : QDoubleValidator(parent)
+{
+}
+
+/*!
+    \qmlproperty string QtQuick2::DoubleValidator::locale
+
+    This property holds the name of the locale used to interpret the number.
 
-    This element provides a validator for non-integer numbers.
+    \sa QML:Qt::locale()
 */
 
+QString QQuickDoubleValidator::localeName() const
+{
+    return locale().name();
+}
+
+void QQuickDoubleValidator::setLocaleName(const QString &name)
+{
+    if (locale().name() != name) {
+        setLocale(QLocale(name));
+        emit localeNameChanged();
+    }
+}
+
+void QQuickDoubleValidator::resetLocaleName()
+{
+    QLocale defaultLocale;
+    if (locale() != defaultLocale) {
+        setLocale(defaultLocale);
+        emit localeNameChanged();
+    }
+}
+
 /*!
     \qmlproperty real QtQuick2::DoubleValidator::top
 
@@ -824,19 +1030,21 @@ void QQuickTextInput::setAutoScroll(bool b)
     The possible values for this property are:
 
     \list
-    \o DoubleValidator.StandardNotation
-    \o DoubleValidator.ScientificNotation (default)
+    \li DoubleValidator.StandardNotation
+    \li DoubleValidator.ScientificNotation (default)
     \endlist
 
     If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2).
 */
 
 /*!
-    \qmlclass RegExpValidator QRegExpValidator
+    \qmltype RegExpValidator
+    \instantiates QRegExpValidator
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
+    \ingroup qtquick-text-utility
+    \brief Provides a string validator
 
-    This element provides a validator, which counts as valid any string which
+    The RegExpValidator type provides a validator, which counts as valid any string which
     matches a specified regular expression.
 */
 /*!
@@ -863,7 +1071,7 @@ void QQuickTextInput::setAutoScroll(bool b)
     input of integers between 11 and 31 into the text input:
 
     \code
-    import QtQuick 1.0
+    import QtQuick 2.0
     TextInput{
         validator: IntValidator{bottom: 11; top: 31;}
         focus: true
@@ -885,16 +1093,51 @@ void QQuickTextInput::setValidator(QValidator* v)
     if (d->m_validator == v)
         return;
 
+    if (d->m_validator) {
+        qmlobject_disconnect(
+                d->m_validator, QValidator, SIGNAL(changed()),
+                this, QQuickTextInput, SLOT(q_validatorChanged()));
+    }
+
     d->m_validator = v;
-    if (!d->hasAcceptableInput(d->m_text)) {
-        d->oldValidity = false;
-        emit acceptableInputChanged();
+
+    if (d->m_validator) {
+        qmlobject_connect(
+                d->m_validator, QValidator, SIGNAL(changed()),
+                this, QQuickTextInput, SLOT(q_validatorChanged()));
     }
 
+    if (isComponentComplete())
+        d->checkIsValid();
+
     emit validatorChanged();
 }
+
+void QQuickTextInput::q_validatorChanged()
+{
+    Q_D(QQuickTextInput);
+    d->checkIsValid();
+}
+
 #endif // QT_NO_VALIDATOR
 
+void QQuickTextInputPrivate::checkIsValid()
+{
+    Q_Q(QQuickTextInput);
+
+    ValidatorState state = hasAcceptableInput(m_text);
+    m_validInput = state != InvalidInput;
+    if (state != AcceptableInput) {
+        if (m_acceptableInput) {
+            m_acceptableInput = false;
+            emit q->acceptableInputChanged();
+        }
+    } else if (!m_acceptableInput) {
+        m_acceptableInput = true;
+        emit q->acceptableInputChanged();
+    }
+}
+
 /*!
     \qmlproperty string QtQuick2::TextInput::inputMask
 
@@ -931,7 +1174,7 @@ void QQuickTextInput::setInputMask(const QString &im)
 bool QQuickTextInput::hasAcceptableInput() const
 {
     Q_D(const QQuickTextInput);
-    return d->hasAcceptableInput(d->m_text);
+    return d->hasAcceptableInput(d->m_text) == QQuickTextInputPrivate::AcceptableInput;
 }
 
 /*!
@@ -943,27 +1186,26 @@ bool QQuickTextInput::hasAcceptableInput() const
     state.
 */
 
-void QQuickTextInputPrivate::updateInputMethodHints()
+Qt::InputMethodHints QQuickTextInputPrivate::effectiveInputMethodHints() const
 {
-    Q_Q(QQuickTextInput);
     Qt::InputMethodHints hints = inputMethodHints;
     if (m_echoMode == QQuickTextInput::Password || m_echoMode == QQuickTextInput::NoEcho)
         hints |= Qt::ImhHiddenText;
     else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
         hints &= ~Qt::ImhHiddenText;
     if (m_echoMode != QQuickTextInput::Normal)
-        hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
-    q->setInputMethodHints(hints);
+        hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
+    return hints;
 }
 /*!
     \qmlproperty enumeration QtQuick2::TextInput::echoMode
 
     Specifies how the text should be displayed in the TextInput.
     \list
-    \o TextInput.Normal - Displays the text as it is. (Default)
-    \o TextInput.Password - Displays asterisks instead of characters.
-    \o TextInput.NoEcho - Displays nothing.
-    \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered
+    \li TextInput.Normal - Displays the text as it is. (Default)
+    \li TextInput.Password - Displays asterisks instead of characters.
+    \li TextInput.NoEcho - Displays nothing.
+    \li TextInput.PasswordEchoOnEdit - Displays characters as they are entered
     while editing, otherwise displays asterisks.
     \endlist
 */
@@ -981,26 +1223,74 @@ void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
     d->cancelPasswordEchoTimer();
     d->m_echoMode = echo;
     d->m_passwordEchoEditing = false;
-    d->updateInputMethodHints();
+    updateInputMethod(Qt::ImHints);
     d->updateDisplayText();
     updateCursorRectangle();
 
     emit echoModeChanged(echoMode());
 }
 
-Qt::InputMethodHints QQuickTextInput::imHints() const
+/*!
+    \qmlproperty enumeration QtQuick2::TextInput::inputMethodHints
+
+    Provides hints to the input method about the expected content of the text input and how it
+    should operate.
+
+    The value is a bit-wise combination of flags, or Qt.ImhNone if no hints are set.
+
+    Flags that alter behaviour are:
+
+    \list
+    \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
+            This is automatically set when setting echoMode to \c TextInput.Password.
+    \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
+            in any persistent storage like predictive user dictionary.
+    \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
+            when a sentence ends.
+    \li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
+    \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
+    \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
+    \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
+
+    \li Qt.ImhDate - The text editor functions as a date field.
+    \li Qt.ImhTime - The text editor functions as a time field.
+    \endlist
+
+    Flags that restrict input (exclusive flags) are:
+
+    \list
+    \li Qt.ImhDigitsOnly - Only digits are allowed.
+    \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
+    \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
+    \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
+    \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
+    \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
+    \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
+    \endlist
+
+    Masks:
+
+    \list
+    \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
+    \endlist
+*/
+
+Qt::InputMethodHints QQuickTextInput::inputMethodHints() const
 {
     Q_D(const QQuickTextInput);
     return d->inputMethodHints;
 }
 
-void QQuickTextInput::setIMHints(Qt::InputMethodHints hints)
+void QQuickTextInput::setInputMethodHints(Qt::InputMethodHints hints)
 {
     Q_D(QQuickTextInput);
-    if (d->inputMethodHints == hints)
+
+    if (hints == d->inputMethodHints)
         return;
+
     d->inputMethodHints = hints;
-    d->updateInputMethodHints();
+    updateInputMethod(Qt::ImHints);
+    emit inputMethodHintsChanged();
 }
 
 /*!
@@ -1013,76 +1303,26 @@ void QQuickTextInput::setIMHints(Qt::InputMethodHints hints)
     needed, and the x property of delegate instance will be set so as
     to be one pixel before the top left of the current character.
 
-    Note that the root item of the delegate component must be a QDeclarativeItem or
-    QDeclarativeItem derived item.
+    Note that the root item of the delegate component must be a QQuickItem or
+    QQuickItem derived item.
 */
-QDeclarativeComponent* QQuickTextInput::cursorDelegate() const
+QQmlComponent* QQuickTextInput::cursorDelegate() const
 {
     Q_D(const QQuickTextInput);
     return d->cursorComponent;
 }
 
-void QQuickTextInput::setCursorDelegate(QDeclarativeComponent* c)
+void QQuickTextInput::setCursorDelegate(QQmlComponent* c)
 {
     Q_D(QQuickTextInput);
-    if (d->cursorComponent == c)
-        return;
-
-    d->cursorComponent = c;
-    if (!c) {
-        //note that the components are owned by something else
-        delete d->cursorItem;
-    } else {
-        d->startCreatingCursor();
-    }
-
-    emit cursorDelegateChanged();
-}
-
-void QQuickTextInputPrivate::startCreatingCursor()
-{
-    Q_Q(QQuickTextInput);
-    if (cursorComponent->isReady()) {
-        q->createCursor();
-    } else if (cursorComponent->isLoading()) {
-        q->connect(cursorComponent, SIGNAL(statusChanged(int)),
-                q, SLOT(createCursor()));
-    } else { // isError
-        qmlInfo(q, cursorComponent->errors()) << QQuickTextInput::tr("Could not load cursor delegate");
-    }
+    QQuickTextUtil::setCursorDelegate(d, c);
 }
 
 void QQuickTextInput::createCursor()
 {
     Q_D(QQuickTextInput);
-    if (!isComponentComplete())
-        return;
-
-    if (d->cursorComponent->isError()) {
-        qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
-        return;
-    }
-
-    if (!d->cursorComponent->isReady())
-        return;
-
-    if (d->cursorItem)
-        delete d->cursorItem;
-    QDeclarativeContext *creationContext = d->cursorComponent->creationContext();
-    QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this));
-    d->cursorItem = qobject_cast<QQuickItem*>(object);
-    if (!d->cursorItem) {
-        delete object;
-        qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
-        return;
-    }
-
-    QRectF r = cursorRectangle();
-
-    QDeclarative_setParent_noEvent(d->cursorItem, this);
-    d->cursorItem->setParentItem(this);
-    d->cursorItem->setPos(r.topLeft());
-    d->cursorItem->setHeight(r.height());
+    d->cursorPending = true;
+    QQuickTextUtil::createCursor(d);
 }
 
 /*!
@@ -1097,11 +1337,13 @@ void QQuickTextInput::createCursor()
 QRectF QQuickTextInput::positionToRectangle(int pos) const
 {
     Q_D(const QQuickTextInput);
-    if (pos > d->m_cursor)
+    if (d->m_echoMode == NoEcho)
+        pos = 0;
+    else if (pos > d->m_cursor)
         pos += d->preeditAreaText().length();
-    QTextLine l = d->m_textLayout.lineAt(0);
+    QTextLine l = d->m_textLayout.lineForTextPosition(pos);
     return l.isValid()
-            ? QRectF(l.cursorToX(pos) - d->hscroll, 0.0, d->m_cursorWidth, l.height())
+            ? QRectF(l.cursorToX(pos) - d->hscroll, l.y() - d->vscroll, 1, l.height())
             : QRectF();
 }
 
@@ -1122,12 +1364,12 @@ QRectF QQuickTextInput::positionToRectangle(int pos) const
     The cursor position type specifies how the cursor position should be resolved.
 
     \list
-    \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x.
-    \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
+    \li TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x.
+    \li TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
     \endlist
 */
 
-void QQuickTextInput::positionAt(QDeclarativeV8Function *args) const
+void QQuickTextInput::positionAt(QQmlV8Function *args) const
 {
     Q_D(const QQuickTextInput);
 
@@ -1163,7 +1405,7 @@ void QQuickTextInput::positionAt(QDeclarativeV8Function *args) const
     args->returnValue(v8::Int32::New(pos));
 }
 
-int QQuickTextInputPrivate::positionAt(int x, int y, QTextLine::CursorPosition position) const
+int QQuickTextInputPrivate::positionAt(qreal x, qreal y, QTextLine::CursorPosition position) const
 {
     x += hscroll;
     y += vscroll;
@@ -1189,7 +1431,7 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
         int cursorPosition = d->m_cursor;
         if (cursorPosition == 0)
             ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
-        if (cursorPosition == text().length())
+        if (!ignore && cursorPosition == text().length())
             ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
     }
     if (ignore) {
@@ -1204,7 +1446,7 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
 void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
 {
     Q_D(QQuickTextInput);
-    const bool wasComposing = d->preeditAreaText().length() > 0;
+    const bool wasComposing = d->hasImState;
     if (d->m_readOnly) {
         ev->ignore();
     } else {
@@ -1213,7 +1455,7 @@ void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
     if (!ev->isAccepted())
         QQuickImplicitSizeItem::inputMethodEvent(ev);
 
-    if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0))
+    if (wasComposing != d->hasImState)
         emit inputMethodComposingChanged();
 }
 
@@ -1227,7 +1469,7 @@ void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event)
         d->selectWordAtPos(cursor);
         event->setAccepted(true);
         if (!d->hasPendingTripleClick()) {
-            d->tripleClickStartPoint = event->localPos().toPoint();
+            d->tripleClickStartPoint = event->localPos();
             d->tripleClickTimer.start();
         }
     } else {
@@ -1243,17 +1485,13 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
 
     d->pressPos = event->localPos();
 
-    if (d->focusOnPress) {
-        bool hadActiveFocus = hasActiveFocus();
-        forceActiveFocus();
-        // re-open input panel on press if already focused
-        if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
-            openSoftwareInputPanel();
-    }
+    if (d->sendMouseEventToInputContext(event))
+        return;
+
     if (d->selectByMouse) {
         setKeepMouseGrab(false);
         d->selectPressed = true;
-        QPoint distanceVector = d->pressPos.toPoint() - d->tripleClickStartPoint;
+        QPointF distanceVector = d->pressPos - d->tripleClickStartPoint;
         if (d->hasPendingTripleClick()
             && distanceVector.manhattanLength() < qApp->styleHints()->startDragDistance()) {
             event->setAccepted(true);
@@ -1262,12 +1500,18 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
         }
     }
 
-    if (d->sendMouseEventToInputContext(event))
-        return;
-
     bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
     int cursor = d->positionAt(event->localPos());
     d->moveCursor(cursor, mark);
+
+    if (d->focusOnPress) {
+        bool hadActiveFocus = hasActiveFocus();
+        forceActiveFocus();
+        // re-open input panel on press if already focused
+        if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
+            qGuiApp->inputMethod()->show();
+    }
+
     event->setAccepted(true);
 }
 
@@ -1325,7 +1569,7 @@ bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event)
         int mousePos = tmp_cursor - m_cursor;
         if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().length()) {
             if (event->type() == QEvent::MouseButtonRelease) {
-                qApp->inputPanel()->invokeAction(QInputPanel::Click, mousePos);
+                qApp->inputMethod()->invokeAction(QInputMethod::Click, mousePos);
             }
             return true;
         }
@@ -1401,9 +1645,11 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
                                   const QRectF &oldGeometry)
 {
     Q_D(QQuickTextInput);
-    if (newGeometry.width() != oldGeometry.width())
-        d->updateLayout();
-    updateCursorRectangle();
+    if (!d->inLayout) {
+        if (newGeometry.width() != oldGeometry.width())
+            d->updateLayout();
+        updateCursorRectangle();
+    }
     QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
 }
 
@@ -1412,14 +1658,20 @@ void QQuickTextInputPrivate::updateHorizontalScroll()
     Q_Q(QQuickTextInput);
     QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor);
     const int preeditLength = m_textLayout.preeditAreaText().length();
-    const int width = q->width();
-    int widthUsed = currentLine.isValid() ? qRound(currentLine.naturalTextWidth()) : 0;
+    const qreal width = qMax<qreal>(0, q->width());
+    qreal cix = 0;
+    qreal widthUsed = 0;
+    if (currentLine.isValid()) {
+        cix = currentLine.cursorToX(m_cursor + preeditLength);
+        const qreal cursorWidth = cix >= 0 ? cix : width - cix;
+        widthUsed = qMax(currentLine.naturalTextWidth(), cursorWidth);
+    }
     int previousScroll = hscroll;
 
     if (!autoScroll || widthUsed <=  width || m_echoMode == QQuickTextInput::NoEcho) {
         hscroll = 0;
     } else {
-        int cix = qRound(currentLine.cursorToX(m_cursor + preeditLength));
+        Q_ASSERT(currentLine.isValid());
         if (cix - hscroll >= width) {
             // text doesn't fit, cursor is to the right of br (scroll right)
             hscroll = cix - width;
@@ -1430,11 +1682,15 @@ void QQuickTextInputPrivate::updateHorizontalScroll()
             // text doesn't fit, text document is to the left of br; align
             // right
             hscroll = widthUsed - width;
+        } else if (width - hscroll > widthUsed) {
+            // text doesn't fit, text document is to the right of br; align
+            // left
+            hscroll = width - widthUsed;
         }
         if (preeditLength > 0) {
             // check to ensure long pre-edit text doesn't push the cursor
             // off to the left
-             cix = qRound(currentLine.cursorToX(m_cursor + qMax(0, m_preeditCursor - 1)));
+             cix = currentLine.cursorToX(m_cursor + qMax(0, m_preeditCursor - 1));
              if (cix < hscroll)
                  hscroll = cix;
         }
@@ -1447,28 +1703,19 @@ void QQuickTextInputPrivate::updateVerticalScroll()
 {
     Q_Q(QQuickTextInput);
     const int preeditLength = m_textLayout.preeditAreaText().length();
-    const int height = q->height();
-    int heightUsed = boundingRect.height();
-    int previousScroll = vscroll;
+    const qreal height = qMax<qreal>(0, q->height());
+    qreal heightUsed = contentSize.height();
+    qreal previousScroll = vscroll;
 
     if (!autoScroll || heightUsed <=  height) {
         // text fits in br; use vscroll for alignment
-        switch (vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask)) {
-        case Qt::AlignBottom:
-            vscroll = heightUsed - height;
-            break;
-        case Qt::AlignVCenter:
-            vscroll = (heightUsed - height) / 2;
-            break;
-        default:
-            // Top
-            vscroll = 0;
-            break;
-        }
+        vscroll = -QQuickTextUtil::alignedY(
+                    heightUsed, height, vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask));
     } else {
-        QRectF r = m_textLayout.lineForTextPosition(m_cursor + preeditLength).rect();
-        int top = qFloor(r.top());
-        int bottom = qCeil(r.bottom());
+        QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + preeditLength);
+        QRectF r = currentLine.isValid() ? currentLine.rect() : QRectF();
+        qreal top = r.top();
+        int bottom = r.bottom();
 
         if (bottom - vscroll >= height) {
             // text doesn't fit, cursor is to the below the br (scroll down)
@@ -1484,53 +1731,70 @@ void QQuickTextInputPrivate::updateVerticalScroll()
         if (preeditLength > 0) {
             // check to ensure long pre-edit text doesn't push the cursor
             // off the top
-             top = qRound(m_textLayout.lineForTextPosition(
-                    m_cursor + qMax(0, m_preeditCursor - 1)).rect().top());
-             if (top < vscroll)
-                 vscroll = top;
+            currentLine = m_textLayout.lineForTextPosition(m_cursor + qMax(0, m_preeditCursor - 1));
+            top = currentLine.isValid() ? currentLine.rect().top() : 0;
+            if (top < vscroll)
+                vscroll = top;
         }
     }
     if (previousScroll != vscroll)
         textLayoutDirty = true;
 }
 
+void QQuickTextInput::triggerPreprocess()
+{
+    Q_D(QQuickTextInput);
+    if (d->updateType == QQuickTextInputPrivate::UpdateNone)
+        d->updateType = QQuickTextInputPrivate::UpdateOnlyPreprocess;
+    update();
+}
+
 QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
 {
     Q_UNUSED(data);
     Q_D(QQuickTextInput);
 
+    if (d->updateType != QQuickTextInputPrivate::UpdatePaintNode && oldNode != 0) {
+        // Update done in preprocess() in the nodes
+        d->updateType = QQuickTextInputPrivate::UpdateNone;
+        return oldNode;
+    }
+
+    d->updateType = QQuickTextInputPrivate::UpdateNone;
+
     QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
     if (node == 0)
-        node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
+        node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
     d->textNode = node;
 
-    if (!d->textLayoutDirty) {
+    if (!d->textLayoutDirty && oldNode != 0) {
         QSGSimpleRectNode *cursorNode = node->cursorNode();
         if (cursorNode != 0 && !isReadOnly()) {
             cursorNode->setRect(cursorRectangle());
 
-            if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
+            if (!d->cursorVisible || d->cursorItem || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
                 d->hideCursor();
             } else {
                 d->showCursor();
             }
         }
     } else {
+        node->setUseNativeRenderer(d->renderType == QQuickTextInput::NativeRendering);
         node->deleteContent();
         node->setMatrix(QMatrix4x4());
 
-        QPoint offset = QPoint(0,0);
-        QFontMetrics fm = QFontMetrics(d->font);
-        if (d->autoScroll) {
+        QPointF offset(0, 0);
+        if (d->autoScroll && d->m_textLayout.lineCount() > 0) {
+            QFontMetricsF fm(d->font);
             // the y offset is there to keep the baseline constant in case we have script changes in the text.
-            offset = -QPoint(d->hscroll, d->vscroll + d->m_ascent - fm.ascent());
+            offset = -QPoint(d->hscroll, d->vscroll + d->m_textLayout.lineAt(0).ascent() - fm.ascent());
         } else {
             offset = -QPoint(d->hscroll, d->vscroll);
         }
 
-        if (!d->m_textLayout.text().isEmpty()) {
+        if (!d->m_textLayout.text().isEmpty() || !d->m_textLayout.preeditAreaText().isEmpty()) {
             node->addTextLayout(offset, &d->m_textLayout, d->color,
-                                QQuickText::Normal, QColor(),
+                                QQuickText::Normal, QColor(), QColor(),
                                 d->selectionColor, d->selectedTextColor,
                                 d->selectionStart(),
                                 d->selectionEnd() - 1); // selectionEnd() returns first char after
@@ -1559,7 +1823,7 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
     case Qt::ImEnabled:
         return QVariant((bool)(flags() & ItemAcceptsInputMethod));
     case Qt::ImHints:
-        return QVariant((int)inputMethodHints());
+        return QVariant((int) d->effectiveInputMethodHints());
     case Qt::ImCursorRectangle:
         return cursorRectangle();
     case Qt::ImFont:
@@ -1589,7 +1853,7 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::deselect()
+    \qmlmethod QtQuick2::TextInput::deselect()
 
     Removes active text selection.
 */
@@ -1600,7 +1864,7 @@ void QQuickTextInput::deselect()
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::selectAll()
+    \qmlmethod QtQuick2::TextInput::selectAll()
 
     Causes all text to be selected.
 */
@@ -1611,7 +1875,7 @@ void QQuickTextInput::selectAll()
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::isRightToLeft(int start, int end)
+    \qmlmethod QtQuick2::TextInput::isRightToLeft(int start, int end)
 
     Returns true if the natural reading direction of the editor text
     found between positions \a start and \a end is right to left.
@@ -1635,8 +1899,10 @@ bool QQuickTextInput::isRightToLeft(int start, int end)
 void QQuickTextInput::cut()
 {
     Q_D(QQuickTextInput);
-    d->copy();
-    d->del();
+    if (!d->m_readOnly) {
+        d->copy();
+        d->del();
+    }
 }
 
 /*!
@@ -1664,30 +1930,200 @@ void QQuickTextInput::paste()
 #endif // QT_NO_CLIPBOARD
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::selectWord()
+    \qmlmethod QtQuick2::TextInput::undo()
 
-    Causes the word closest to the current cursor position to be selected.
+    Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
+    current selection, and updates the selection start to the current cursor
+    position.
 */
-void QQuickTextInput::selectWord()
+
+void QQuickTextInput::undo()
 {
     Q_D(QQuickTextInput);
-    d->selectWordAtPos(d->m_cursor);
+    if (!d->m_readOnly) {
+        d->internalUndo();
+        d->finishChange(-1, true);
+    }
 }
 
 /*!
-    \qmlproperty bool QtQuick2::TextInput::smooth
+    \qmlmethod QtQuick2::TextInput::redo()
 
-    This property holds whether the text is smoothly scaled or transformed.
+    Redoes the last operation if redo is \l {canRedo}{available}.
+*/
 
-    Smooth filtering gives better visual quality, but is slower.  If
-    the item is displayed at its natural size, this property has no visual or
-    performance effect.
+void QQuickTextInput::redo()
+{
+    Q_D(QQuickTextInput);
+    if (!d->m_readOnly) {
+        d->internalRedo();
+        d->finishChange();
+    }
+}
+
+/*!
+    \qmlmethod QtQuick2::TextInput::insert(int position, string text)
 
-    \note Generally scaling artifacts are only visible if the item is stationary on
-    the screen.  A common pattern when animating an item is to disable smooth
-    filtering at the beginning of the animation and reenable it at the conclusion.
+    Inserts \a text into the TextInput at position.
 */
 
+void QQuickTextInput::insert(int position, const QString &text)
+{
+    Q_D(QQuickTextInput);
+    if (d->m_echoMode == QQuickTextInput::Password) {
+        int delay = qGuiApp->styleHints()->passwordMaskDelay();
+        if (delay > 0)
+            d->m_passwordEchoTimer.start(delay, this);
+    }
+    if (position < 0 || position > d->m_text.length())
+        return;
+
+    const int priorState = d->m_undoState;
+
+    QString insertText = text;
+
+    if (d->hasSelectedText()) {
+        d->addCommand(QQuickTextInputPrivate::Command(
+                QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
+    }
+    if (d->m_maskData) {
+        insertText = d->maskString(position, insertText);
+        for (int i = 0; i < insertText.length(); ++i) {
+            d->addCommand(QQuickTextInputPrivate::Command(
+                    QQuickTextInputPrivate::DeleteSelection, position + i, d->m_text.at(position + i), -1, -1));
+            d->addCommand(QQuickTextInputPrivate::Command(
+                    QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
+        }
+        d->m_text.replace(position, insertText.length(), insertText);
+        if (!insertText.isEmpty())
+            d->m_textDirty = true;
+        if (position < d->m_selend && position + insertText.length() > d->m_selstart)
+            d->m_selDirty = true;
+    } else {
+        int remaining = d->m_maxLength - d->m_text.length();
+        if (remaining != 0) {
+            insertText = insertText.left(remaining);
+            d->m_text.insert(position, insertText);
+            for (int i = 0; i < insertText.length(); ++i)
+               d->addCommand(QQuickTextInputPrivate::Command(
+                    QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
+            if (d->m_cursor >= position)
+                d->m_cursor += insertText.length();
+            if (d->m_selstart >= position)
+                d->m_selstart += insertText.length();
+            if (d->m_selend >= position)
+                d->m_selend += insertText.length();
+            d->m_textDirty = true;
+            if (position >= d->m_selstart && position <= d->m_selend)
+                d->m_selDirty = true;
+        }
+    }
+
+    d->addCommand(QQuickTextInputPrivate::Command(
+            QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
+    d->finishChange(priorState);
+
+    if (d->lastSelectionStart != d->lastSelectionEnd) {
+        if (d->m_selstart != d->lastSelectionStart) {
+            d->lastSelectionStart = d->m_selstart;
+            emit selectionStartChanged();
+        }
+        if (d->m_selend != d->lastSelectionEnd) {
+            d->lastSelectionEnd = d->m_selend;
+            emit selectionEndChanged();
+        }
+    }
+}
+
+/*!
+    \qmlmethod string QtQuick2::TextInput::getText(int start, int end)
+
+    Removes the section of text that is between the \a start and \a end positions from the TextInput.
+*/
+
+void QQuickTextInput::remove(int start, int end)
+{
+    Q_D(QQuickTextInput);
+
+    start = qBound(0, start, d->m_text.length());
+    end = qBound(0, end, d->m_text.length());
+
+    if (start > end)
+        qSwap(start, end);
+    else if (start == end)
+        return;
+
+    if (start < d->m_selend && end > d->m_selstart)
+        d->m_selDirty = true;
+
+    const int priorState = d->m_undoState;
+
+    d->addCommand(QQuickTextInputPrivate::Command(
+            QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
+
+    if (start <= d->m_cursor && d->m_cursor < end) {
+        // cursor is within the selection. Split up the commands
+        // to be able to restore the correct cursor position
+        for (int i = d->m_cursor; i >= start; --i) {
+            d->addCommand(QQuickTextInputPrivate::Command(
+                    QQuickTextInputPrivate::DeleteSelection, i, d->m_text.at(i), -1, 1));
+        }
+        for (int i = end - 1; i > d->m_cursor; --i) {
+            d->addCommand(QQuickTextInputPrivate::Command(
+                    QQuickTextInputPrivate::DeleteSelection, i - d->m_cursor + start - 1, d->m_text.at(i), -1, -1));
+        }
+    } else {
+        for (int i = end - 1; i >= start; --i) {
+            d->addCommand(QQuickTextInputPrivate::Command(
+                    QQuickTextInputPrivate::RemoveSelection, i, d->m_text.at(i), -1, -1));
+        }
+    }
+    if (d->m_maskData) {
+        d->m_text.replace(start, end - start,  d->clearString(start, end - start));
+        for (int i = 0; i < end - start; ++i) {
+            d->addCommand(QQuickTextInputPrivate::Command(
+                    QQuickTextInputPrivate::Insert, start + i, d->m_text.at(start + i), -1, -1));
+        }
+    } else {
+        d->m_text.remove(start, end - start);
+
+        if (d->m_cursor > start)
+            d->m_cursor -= qMin(d->m_cursor, end) - start;
+        if (d->m_selstart > start)
+            d->m_selstart -= qMin(d->m_selstart, end) - start;
+        if (d->m_selend > end)
+            d->m_selend -= qMin(d->m_selend, end) - start;
+    }
+    d->addCommand(QQuickTextInputPrivate::Command(
+            QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
+
+    d->m_textDirty = true;
+    d->finishChange(priorState);
+
+    if (d->lastSelectionStart != d->lastSelectionEnd) {
+        if (d->m_selstart != d->lastSelectionStart) {
+            d->lastSelectionStart = d->m_selstart;
+            emit selectionStartChanged();
+        }
+        if (d->m_selend != d->lastSelectionEnd) {
+            d->lastSelectionEnd = d->m_selend;
+            emit selectionEndChanged();
+        }
+    }
+}
+
+
+/*!
+    \qmlmethod QtQuick2::TextInput::selectWord()
+
+    Causes the word closest to the current cursor position to be selected.
+*/
+void QQuickTextInput::selectWord()
+{
+    Q_D(QQuickTextInput);
+    d->selectWordAtPos(d->m_cursor);
+}
+
 /*!
    \qmlproperty string QtQuick2::TextInput::passwordCharacter
 
@@ -1757,48 +2193,124 @@ void QQuickTextInput::setSelectByMouse(bool on)
 }
 
 /*!
-    \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode
+    \qmlproperty enumeration QtQuick2::TextInput::mouseSelectionMode
 
     Specifies how text should be selected using a mouse.
 
     \list
-    \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
-    \o TextInput.SelectWords - The selection is updated with whole words.
+    \li TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
+    \li TextInput.SelectWords - The selection is updated with whole words.
     \endlist
 
-    This property only applies when \l selectByMouse is true.
+    This property only applies when \l selectByMouse is true.
+*/
+
+QQuickTextInput::SelectionMode QQuickTextInput::mouseSelectionMode() const
+{
+    Q_D(const QQuickTextInput);
+    return d->mouseSelectionMode;
+}
+
+void QQuickTextInput::setMouseSelectionMode(SelectionMode mode)
+{
+    Q_D(QQuickTextInput);
+    if (d->mouseSelectionMode != mode) {
+        d->mouseSelectionMode = mode;
+        emit mouseSelectionModeChanged(mode);
+    }
+}
+
+/*!
+    \qmlproperty bool QtQuick2::TextInput::persistentSelection
+
+    Whether the TextInput should keep its selection when it loses active focus to another
+    item in the scene. By default this is set to false;
+*/
+
+bool QQuickTextInput::persistentSelection() const
+{
+    Q_D(const QQuickTextInput);
+    return d->persistentSelection;
+}
+
+void QQuickTextInput::setPersistentSelection(bool on)
+{
+    Q_D(QQuickTextInput);
+    if (d->persistentSelection == on)
+        return;
+    d->persistentSelection = on;
+    emit persistentSelectionChanged();
+}
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+    \qmlproperty bool QtQuick2::TextInput::canPaste
+
+    Returns true if the TextInput is writable and the content of the clipboard is
+    suitable for pasting into the TextInput.
+*/
+bool QQuickTextInput::canPaste() const
+{
+    Q_D(const QQuickTextInput);
+    if (!d->canPasteValid) {
+        if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
+            const_cast<QQuickTextInputPrivate *>(d)->canPaste = !d->m_readOnly && mimeData->hasText();
+        const_cast<QQuickTextInputPrivate *>(d)->canPasteValid = true;
+    }
+    return d->canPaste;
+}
+#endif
+
+/*!
+    \qmlproperty bool QtQuick2::TextInput::canUndo
+
+    Returns true if the TextInput is writable and there are previous operations
+    that can be undone.
+*/
+
+bool QQuickTextInput::canUndo() const
+{
+    Q_D(const QQuickTextInput);
+    return d->canUndo;
+}
+
+/*!
+    \qmlproperty bool QtQuick2::TextInput::canRedo
+
+    Returns true if the TextInput is writable and there are \l {undo}{undone}
+    operations that can be redone.
 */
 
-QQuickTextInput::SelectionMode QQuickTextInput::mouseSelectionMode() const
+bool QQuickTextInput::canRedo() const
 {
     Q_D(const QQuickTextInput);
-    return d->mouseSelectionMode;
+    return d->canRedo;
 }
 
-void QQuickTextInput::setMouseSelectionMode(SelectionMode mode)
+/*!
+    \qmlproperty real QtQuick2::TextInput::contentWidth
+
+    Returns the width of the text, including the width past the width
+    which is covered due to insufficient wrapping if \l wrapMode is set.
+*/
+
+qreal QQuickTextInput::contentWidth() const
 {
-    Q_D(QQuickTextInput);
-    if (d->mouseSelectionMode != mode) {
-        d->mouseSelectionMode = mode;
-        emit mouseSelectionModeChanged(mode);
-    }
+    Q_D(const QQuickTextInput);
+    return d->contentSize.width();
 }
 
 /*!
-    \qmlproperty bool QtQuick2::TextInput::canPaste
+    \qmlproperty real QtQuick2::TextInput::contentHeight
 
-    Returns true if the TextInput is writable and the content of the clipboard is
-    suitable for pasting into the TextEdit.
+    Returns the height of the text, including the height past the height
+    that is covered if the text does not fit within the set height.
 */
-bool QQuickTextInput::canPaste() const
+
+qreal QQuickTextInput::contentHeight() const
 {
     Q_D(const QQuickTextInput);
-    if (!d->canPasteValid) {
-        if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
-            const_cast<QQuickTextInputPrivate *>(d)->canPaste = !d->m_readOnly && mimeData->hasText();
-        const_cast<QQuickTextInputPrivate *>(d)->canPasteValid = true;
-    }
-    return d->canPaste;
+    return d->contentSize.height();
 }
 
 void QQuickTextInput::moveCursorSelection(int position)
@@ -1808,7 +2320,7 @@ void QQuickTextInput::moveCursorSelection(int position)
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters)
+    \qmlmethod QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters)
 
     Moves the cursor to \a position and updates the selection according to the optional \a mode
     parameter.  (To only move the cursor, set the \l cursorPosition property.)
@@ -1822,9 +2334,9 @@ void QQuickTextInput::moveCursorSelection(int position)
     basis.  If not specified the selection mode will default to TextInput.SelectCharacters.
 
     \list
-    \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
+    \li TextInput.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
     the previous cursor position) to the specified position.
-    \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
+    \li TextInput.SelectWords - Sets the selectionStart and selectionEnd to include all
     words between the specified position and the previous cursor position.  Words partially in the
     range are included.
     \endlist
@@ -1866,8 +2378,8 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
             finder.setPosition(anchor);
 
             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
-            if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord)
-                    || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) {
+            if (anchor < text.length() && (reasons == QTextBoundaryFinder::NotAtBoundary
+                                           || (reasons & QTextBoundaryFinder::EndOfItem))) {
                 finder.toPreviousBoundary();
             }
             anchor = finder.position() != -1 ? finder.position() : 0;
@@ -1884,11 +2396,10 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
             finder.setPosition(anchor);
 
             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
-            if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord)
-                    || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) {
+            if (anchor > 0 && (reasons == QTextBoundaryFinder::NotAtBoundary
+                               || (reasons & QTextBoundaryFinder::StartOfItem))) {
                 finder.toNextBoundary();
             }
-
             anchor = finder.position() != -1 ? finder.position() : text.length();
 
             finder.setPosition(pos);
@@ -1901,97 +2412,11 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
     }
 }
 
-/*!
-    \qmlmethod void QtQuick2::TextInput::openSoftwareInputPanel()
-
-    Opens software input panels like virtual keyboards for typing, useful for
-    customizing when you want the input keyboard to be shown and hidden in
-    your application.
-
-    By default the opening of input panels follows the platform style. Input panels are
-    always closed if no editor has active focus.
-
-    You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
-    and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
-    the behavior you want.
-
-    Only relevant on platforms, which provide virtual keyboards.
-
-    \qml
-        import QtQuick 1.0
-        TextInput {
-            id: textInput
-            text: "Hello world!"
-            activeFocusOnPress: false
-            MouseArea {
-                anchors.fill: parent
-                onClicked: {
-                    if (!textInput.activeFocus) {
-                        textInput.forceActiveFocus()
-                        textInput.openSoftwareInputPanel();
-                    } else {
-                        textInput.focus = false;
-                    }
-                }
-                onPressAndHold: textInput.closeSoftwareInputPanel();
-            }
-        }
-    \endqml
-*/
-void QQuickTextInput::openSoftwareInputPanel()
-{
-    if (qGuiApp)
-        qGuiApp->inputPanel()->show();
-}
-
-/*!
-    \qmlmethod void QtQuick2::TextInput::closeSoftwareInputPanel()
-
-    Closes a software input panel like a virtual keyboard shown on the screen, useful
-    for customizing when you want the input keyboard to be shown and hidden in
-    your application.
-
-    By default the opening of input panels follows the platform style. Input panels are
-    always closed if no editor has active focus.
-
-    You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
-    and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
-    the behavior you want.
-
-    Only relevant on platforms, which provide virtual keyboards.
-
-    \qml
-        import QtQuick 1.0
-        TextInput {
-            id: textInput
-            text: "Hello world!"
-            activeFocusOnPress: false
-            MouseArea {
-                anchors.fill: parent
-                onClicked: {
-                    if (!textInput.activeFocus) {
-                        textInput.forceActiveFocus();
-                        textInput.openSoftwareInputPanel();
-                    } else {
-                        textInput.focus = false;
-                    }
-                }
-                onPressAndHold: textInput.closeSoftwareInputPanel();
-            }
-        }
-    \endqml
-*/
-void QQuickTextInput::closeSoftwareInputPanel()
-{
-    if (qGuiApp)
-        qGuiApp->inputPanel()->hide();
-}
-
 void QQuickTextInput::focusInEvent(QFocusEvent *event)
 {
     Q_D(const QQuickTextInput);
     if (d->focusOnPress && !d->m_readOnly)
-        openSoftwareInputPanel();
+        qGuiApp->inputMethod()->show();
     QQuickImplicitSizeItem::focusInEvent(event);
 }
 
@@ -2000,19 +2425,20 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
     Q_D(QQuickTextInput);
     if (change == ItemActiveFocusHasChanged) {
         bool hasFocus = value.boolValue;
-        d->focused = hasFocus;
-        setCursorVisible(hasFocus); // ### refactor:  && d->canvas && d->canvas->hasFocus()
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
+        setCursorVisible(hasFocus);
         if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) {
-#else
-        if (!hasFocus && d->m_passwordEchoEditing) {
-#endif
             d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
         }
 
         if (!hasFocus) {
-            d->commitPreedit();
-            d->deselect();
+            if (!d->persistentSelection)
+                d->deselect();
+            disconnect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
+                       this, SLOT(q_updateAlignment()));
+        } else {
+            q_updateAlignment();
+            connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
+                    this, SLOT(q_updateAlignment()));
         }
     }
     QQuickItem::itemChange(change, value);
@@ -2033,33 +2459,33 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
 bool QQuickTextInput::isInputMethodComposing() const
 {
     Q_D(const QQuickTextInput);
-    return d->preeditAreaText().length() > 0;
+    return d->hasImState;
 }
 
 void QQuickTextInputPrivate::init()
 {
     Q_Q(QQuickTextInput);
-    q->setSmooth(smooth);
-    q->setAcceptedMouseButtons(Qt::LeftButton);
+#ifndef QT_NO_CLIPBOARD
+    if (QGuiApplication::clipboard()->supportsSelection())
+        q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
+    else
+#endif
+        q->setAcceptedMouseButtons(Qt::LeftButton);
+
     q->setFlag(QQuickItem::ItemAcceptsInputMethod);
     q->setFlag(QQuickItem::ItemHasContents);
 #ifndef QT_NO_CLIPBOARD
-    q->connect(q, SIGNAL(readOnlyChanged(bool)),
-            q, SLOT(q_canPasteChanged()));
     q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()),
             q, SLOT(q_canPasteChanged()));
 #endif // QT_NO_CLIPBOARD
 
-    oldValidity = hasAcceptableInput(m_text);
     lastSelectionStart = 0;
     lastSelectionEnd = 0;
-    selectedTextColor = m_palette.color(QPalette::HighlightedText);
-    selectionColor = m_palette.color(QPalette::Highlight);
     determineHorizontalAlignment();
 
     if (!qmlDisableDistanceField()) {
         QTextOption option = m_textLayout.textOption();
-        option.setUseDesignMetrics(true);
+        option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
         m_textLayout.setTextOption(option);
     }
 }
@@ -2072,20 +2498,22 @@ void QQuickTextInput::updateCursorRectangle()
 
     d->updateHorizontalScroll();
     d->updateVerticalScroll();
+    d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
     update();
-    updateMicroFocus();
     emit cursorRectangleChanged();
     if (d->cursorItem) {
         QRectF r = cursorRectangle();
         d->cursorItem->setPos(r.topLeft());
         d->cursorItem->setHeight(r.height());
     }
+    updateInputMethod(Qt::ImCursorRectangle);
 }
 
 void QQuickTextInput::selectionChanged()
 {
     Q_D(QQuickTextInput);
     d->textLayoutDirty = true; //TODO: Only update rect in selection
+    d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
     update();
     emit selectedTextChanged();
 
@@ -2119,13 +2547,27 @@ QRectF QQuickTextInput::boundingRect() const
 {
     Q_D(const QQuickTextInput);
 
-    QRectF r = d->boundingRect;
-    int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->m_cursorWidth;
+    int cursorWidth = d->cursorItem ? 0 : 1;
+
+    qreal hscroll = d->hscroll;
+    if (!d->autoScroll || d->contentSize.width() < width())
+        hscroll -= QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign());
 
     // Could include font max left/right bearings to either side of rectangle.
+    QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height());
+    r.setRight(r.right() + cursorWidth);
+    return r;
+}
+
+QRectF QQuickTextInput::clipRect() const
+{
+    Q_D(const QQuickTextInput);
 
+    int cursorWidth = d->cursorItem ? d->cursorItem->width() : 1;
+
+    // Could include font max left/right bearings to either side of rectangle.
+    QRectF r = QQuickImplicitSizeItem::clipRect();
     r.setRight(r.right() + cursorWidth);
-    r.translate(-d->hscroll, -d->vscroll);
     return r;
 }
 
@@ -2147,6 +2589,15 @@ void QQuickTextInput::q_canPasteChanged()
 
 }
 
+void QQuickTextInput::q_updateAlignment()
+{
+    Q_D(QQuickTextInput);
+    if (d->determineHorizontalAlignment()) {
+        d->updateLayout();
+        updateCursorRectangle();
+    }
+}
+
 // ### these should come from QStyleHints
 const int textCursorWidth = 1;
 const bool fullWidthSelection = true;
@@ -2168,7 +2619,6 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
 
     if (m_echoMode == QQuickTextInput::Password) {
          str.fill(m_passwordCharacter);
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
         if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.length()) {
             int cursor = m_cursor - 1;
             QChar uc = m_text.at(cursor);
@@ -2181,7 +2631,6 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
                     str[cursor - 1] = uc;
             }
         }
-#endif
     } else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
         str.fill(m_passwordCharacter);
     }
@@ -2205,6 +2654,38 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
     }
 }
 
+qreal QQuickTextInputPrivate::getImplicitWidth() const
+{
+    Q_Q(const QQuickTextInput);
+    if (!requireImplicitWidth) {
+        QQuickTextInputPrivate *d = const_cast<QQuickTextInputPrivate *>(this);
+        d->requireImplicitWidth = true;
+
+        if (q->isComponentComplete()) {
+            // One time cost, only incurred if implicitWidth is first requested after
+            // componentComplete.
+            QTextLayout layout(m_text);
+
+            QTextOption option = m_textLayout.textOption();
+            option.setTextDirection(m_layoutDirection);
+            option.setFlags(QTextOption::IncludeTrailingSpaces);
+            option.setWrapMode(QTextOption::WrapMode(wrapMode));
+            option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
+            layout.setTextOption(option);
+            layout.setFont(font);
+            layout.setPreeditArea(m_textLayout.preeditAreaPosition(), m_textLayout.preeditAreaText());
+            layout.beginLayout();
+
+            QTextLine line = layout.createLine();
+            line.setLineWidth(INT_MAX);
+            d->implicitWidth = qCeil(line.naturalTextWidth());
+
+            layout.endLayout();
+        }
+    }
+    return implicitWidth;
+}
+
 void QQuickTextInputPrivate::updateLayout()
 {
     Q_Q(QQuickTextInput);
@@ -2212,26 +2693,36 @@ void QQuickTextInputPrivate::updateLayout()
     if (!q->isComponentComplete())
         return;
 
+
     QTextOption option = m_textLayout.textOption();
-    option.setTextDirection(m_layoutDirection);
-    option.setFlags(QTextOption::IncludeTrailingSpaces);
+    option.setTextDirection(layoutDirection());
     option.setWrapMode(QTextOption::WrapMode(wrapMode));
     option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
     m_textLayout.setTextOption(option);
     m_textLayout.setFont(font);
 
-    boundingRect = QRectF();
     m_textLayout.beginLayout();
+
     QTextLine line = m_textLayout.createLine();
+    if (requireImplicitWidth) {
+        line.setLineWidth(INT_MAX);
+        const bool wasInLayout = inLayout;
+        inLayout = true;
+        q->setImplicitWidth(qCeil(line.naturalTextWidth()));
+        inLayout = wasInLayout;
+        if (inLayout)       // probably the result of a binding loop, but by letting it
+            return;         // get this far we'll get a warning to that effect.
+    }
     qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
     qreal height = 0;
-    QTextLine firstLine = line;
+    qreal width = 0;
     do {
         line.setLineWidth(lineWidth);
-        line.setPosition(QPointF(line.position().x(), height));
-        boundingRect = boundingRect.united(line.naturalTextRect());
+        line.setPosition(QPointF(0, height));
 
         height += line.height();
+        width = qMax(width, line.naturalTextWidth());
+
         line = m_textLayout.createLine();
     } while (line.isValid());
     m_textLayout.endLayout();
@@ -2239,12 +2730,21 @@ void QQuickTextInputPrivate::updateLayout()
     option.setWrapMode(QTextOption::NoWrap);
     m_textLayout.setTextOption(option);
 
-    m_ascent = qRound(firstLine.ascent());
     textLayoutDirty = true;
 
+    const QSizeF previousSize = contentSize;
+    contentSize = QSizeF(width, height);
+
+    updateType = UpdatePaintNode;
     q->update();
-    q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
 
+    if (!requireImplicitWidth && !q->widthValid())
+        q->setImplicitSize(width, height);
+    else
+        q->setImplicitHeight(height);
+
+    if (previousSize != contentSize)
+        emit q->contentSizeChanged();
 }
 
 #ifndef QT_NO_CLIPBOARD
@@ -2288,26 +2788,34 @@ void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode)
 
 /*!
     \internal
-
-    Exits preedit mode and commits parts marked as tentative commit
 */
 void QQuickTextInputPrivate::commitPreedit()
 {
-    if (!composeMode())
+    Q_Q(QQuickTextInput);
+
+    if (!hasImState)
         return;
 
-    qApp->inputPanel()->reset();
+    qApp->inputMethod()->commit();
 
-    if (!m_tentativeCommit.isEmpty()) {
-        internalInsert(m_tentativeCommit);
-        m_tentativeCommit.clear();
-        finishChange(-1, true/*not used, not documented*/, false);
-    }
+    if (!hasImState)
+        return;
+
+    QInputMethodEvent ev;
+    QCoreApplication::sendEvent(q, &ev);
+}
+
+void QQuickTextInputPrivate::cancelPreedit()
+{
+    Q_Q(QQuickTextInput);
 
-    m_preeditCursor = 0;
-    m_textLayout.setPreeditArea(-1, QString());
-    m_textLayout.clearAdditionalFormats();
-    updateLayout();
+    if (!hasImState)
+        return;
+
+    qApp->inputMethod()->reset();
+
+    QInputMethodEvent ev;
+    QCoreApplication::sendEvent(q, &ev);
 }
 
 /*!
@@ -2322,7 +2830,7 @@ void QQuickTextInputPrivate::commitPreedit()
 void QQuickTextInputPrivate::backspace()
 {
     int priorState = m_undoState;
-    if (hasSelectedText()) {
+    if (separateSelection()) {
         removeSelectedText();
     } else if (m_cursor) {
             --m_cursor;
@@ -2355,7 +2863,7 @@ void QQuickTextInputPrivate::backspace()
 void QQuickTextInputPrivate::del()
 {
     int priorState = m_undoState;
-    if (hasSelectedText()) {
+    if (separateSelection()) {
         removeSelectedText();
     } else {
         int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
@@ -2375,7 +2883,8 @@ void QQuickTextInputPrivate::del()
 void QQuickTextInputPrivate::insert(const QString &newText)
 {
     int priorState = m_undoState;
-    removeSelectedText();
+    if (separateSelection())
+        removeSelectedText();
     internalInsert(newText);
     finishChange(priorState);
 }
@@ -2388,6 +2897,7 @@ void QQuickTextInputPrivate::insert(const QString &newText)
 void QQuickTextInputPrivate::clear()
 {
     int priorState = m_undoState;
+    separateSelection();
     m_selstart = 0;
     m_selend = m_text.length();
     removeSelectedText();
@@ -2436,19 +2946,8 @@ void QQuickTextInputPrivate::setSelection(int start, int length)
     }
     emit q->selectionChanged();
     emitCursorPositionChanged();
-}
-
-/*!
-    \internal
-
-    Initializes the line control with a starting text value of \a txt.
-*/
-void QQuickTextInputPrivate::init(const QString &txt)
-{
-    m_text = txt;
-
-    updateDisplayText();
-    m_cursor = m_text.length();
+    q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
+                        | Qt::ImCursorPosition | Qt::ImCurrentSelection);
 }
 
 /*!
@@ -2525,6 +3024,7 @@ void QQuickTextInputPrivate::moveCursor(int pos, bool mark)
         emit q->selectionChanged();
     }
     emitCursorPositionChanged();
+    q->updateInputMethod();
 }
 
 /*!
@@ -2548,6 +3048,7 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
     if (isGettingInput) {
         // If any text is being input, remove selected text.
         priorState = m_undoState;
+        separateSelection();
         if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
             updatePasswordEchoEditing(true);
             m_selstart = 0;
@@ -2600,14 +3101,17 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
 #endif //QT_NO_IM
     const int oldPreeditCursor = m_preeditCursor;
     m_preeditCursor = event->preeditString().length();
-    m_hideCursor = false;
+    hasImState = !event->preeditString().isEmpty();
+    bool cursorVisible = true;
     QList<QTextLayout::FormatRange> formats;
     for (int i = 0; i < event->attributes().size(); ++i) {
         const QInputMethodEvent::Attribute &a = event->attributes().at(i);
         if (a.type == QInputMethodEvent::Cursor) {
+            hasImState = true;
             m_preeditCursor = a.start;
-            m_hideCursor = !a.length;
+            cursorVisible = a.length != 0;
         } else if (a.type == QInputMethodEvent::TextFormat) {
+            hasImState = true;
             QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
             if (f.isValid()) {
                 QTextLayout::FormatRange o;
@@ -2621,23 +3125,22 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
     m_textLayout.setAdditionalFormats(formats);
 
     updateDisplayText(/*force*/ true);
-    if (cursorPositionChanged)
-        emitCursorPositionChanged();
-    else if (m_preeditCursor != oldPreeditCursor)
+    if ((cursorPositionChanged && !emitCursorPositionChanged())
+            || m_preeditCursor != oldPreeditCursor
+            || isGettingInput) {
         q->updateCursorRectangle();
-
-    bool tentativeCommitChanged = m_tentativeCommit != event->tentativeCommitString();
-
-    if (tentativeCommitChanged) {
-        m_textDirty = true;
-        m_tentativeCommit = event->tentativeCommitString();
     }
 
-    if (isGettingInput || tentativeCommitChanged)
+    if (isGettingInput)
         finishChange(priorState);
 
-    if (selectionChange)
+    q->setCursorVisible(cursorVisible);
+
+    if (selectionChange) {
         emit q->selectionChanged();
+        q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
+                            | Qt::ImCursorPosition | Qt::ImCurrentSelection);
+    }
 }
 
 /*!
@@ -2678,31 +3181,28 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
     Q_Q(QQuickTextInput);
 
     Q_UNUSED(update)
+    bool inputMethodAttributesChanged = m_textDirty || m_selDirty;
+    bool alignmentChanged = false;
 
     if (m_textDirty) {
         // do validation
         bool wasValidInput = m_validInput;
+        bool wasAcceptable = m_acceptableInput;
         m_validInput = true;
+        m_acceptableInput = true;
 #ifndef QT_NO_VALIDATOR
         if (m_validator) {
             QString textCopy = m_text;
             int cursorCopy = m_cursor;
-            m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
+            QValidator::State state = m_validator->validate(textCopy, cursorCopy);
+            m_validInput = state != QValidator::Invalid;
+            m_acceptableInput = state == QValidator::Acceptable;
             if (m_validInput) {
                 if (m_text != textCopy) {
                     internalSetText(textCopy, cursorCopy);
                     return true;
                 }
                 m_cursor = cursorCopy;
-
-                if (!m_tentativeCommit.isEmpty()) {
-                    textCopy.insert(m_cursor, m_tentativeCommit);
-                    bool validInput = m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid;
-                    if (!validInput)
-                        m_tentativeCommit.clear();
-                }
-            } else {
-                m_tentativeCommit.clear();
             }
         }
 #endif
@@ -2711,32 +3211,44 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
                 return false;
             internalUndo(validateFromState);
             m_history.resize(m_undoState);
-            if (m_modifiedState > m_undoState)
-                m_modifiedState = -1;
             m_validInput = true;
+            m_acceptableInput = wasAcceptable;
             m_textDirty = false;
         }
-        updateDisplayText();
 
         if (m_textDirty) {
             m_textDirty = false;
             m_preeditDirty = false;
-            determineHorizontalAlignment();
+            alignmentChanged = determineHorizontalAlignment();
             emit q->textChanged();
         }
 
-        if (m_validInput != wasValidInput)
+        updateDisplayText(alignmentChanged);
+
+        if (m_acceptableInput != wasAcceptable)
             emit q->acceptableInputChanged();
     }
     if (m_preeditDirty) {
         m_preeditDirty = false;
-        determineHorizontalAlignment();
+        if (determineHorizontalAlignment()) {
+            alignmentChanged = true;
+            updateLayout();
+        }
     }
+
     if (m_selDirty) {
         m_selDirty = false;
         emit q->selectionChanged();
     }
-    emitCursorPositionChanged();
+
+    inputMethodAttributesChanged |= (m_cursor != m_lastCursorPos);
+    if (inputMethodAttributesChanged)
+        q->updateInputMethod();
+    emitUndoRedoChanged();
+
+    if (!emitCursorPositionChanged() && alignmentChanged)
+        q->updateCursorRectangle();
+
     return true;
 }
 
@@ -2757,7 +3269,7 @@ void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool e
         m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
     }
     m_history.clear();
-    m_modifiedState =  m_undoState = 0;
+    m_undoState = 0;
     m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
     m_textDirty = (oldText != m_text);
 
@@ -2765,8 +3277,10 @@ void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool e
 #ifdef QT_NO_ACCESSIBILITY
     Q_UNUSED(changed)
 #else
-    if (changed)
-        QAccessible::updateAccessibility(q, 0, QAccessible::TextUpdated);
+    if (changed) {
+        QAccessibleTextUpdateEvent ev(q, 0, oldText, m_text);
+        QAccessible::updateAccessibility(&ev);
+    }
 #endif
 }
 
@@ -2801,13 +3315,13 @@ void QQuickTextInputPrivate::addCommand(const Command &cmd)
 */
 void QQuickTextInputPrivate::internalInsert(const QString &s)
 {
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
     Q_Q(QQuickTextInput);
-    if (m_echoMode == QQuickTextInput::Password)
-        m_passwordEchoTimer.start(qt_passwordEchoDelay, q);
-#endif
-    if (hasSelectedText())
-        addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+    if (m_echoMode == QQuickTextInput::Password) {
+        int delay = qGuiApp->styleHints()->passwordMaskDelay();
+        if (delay > 0)
+            m_passwordEchoTimer.start(delay, q);
+    }
+    Q_ASSERT(!hasSelectedText());   // insert(), processInputMethodEvent() call removeSelectedText() first.
     if (m_maskData) {
         QString ms = maskString(m_cursor, s);
         for (int i = 0; i < (int) ms.length(); ++i) {
@@ -2844,8 +3358,7 @@ void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
 {
     if (m_cursor < (int) m_text.length()) {
         cancelPasswordEchoTimer();
-        if (hasSelectedText())
-            addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+        Q_ASSERT(!hasSelectedText());   // del(), backspace() call removeSelectedText() first.
         addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
                    m_cursor, m_text.at(m_cursor), -1, -1));
         if (m_maskData) {
@@ -2871,9 +3384,7 @@ void QQuickTextInputPrivate::removeSelectedText()
 {
     if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
         cancelPasswordEchoTimer();
-        separate();
         int i ;
-        addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
         if (m_selstart <= m_cursor && m_cursor < m_selend) {
             // cursor is within the selection. Split up the commands
             // to be able to restore the correct cursor position
@@ -2902,6 +3413,25 @@ void QQuickTextInputPrivate::removeSelectedText()
 /*!
     \internal
 
+    Adds the current selection to the undo history.
+
+    Returns true if there is a current selection and false otherwise.
+*/
+
+bool QQuickTextInputPrivate::separateSelection()
+{
+    if (hasSelectedText()) {
+        separate();
+        addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/*!
+    \internal
+
     Parses the input mask specified by \a maskFields to generate
     the mask data used to handle input masks.
 */
@@ -3085,32 +3615,34 @@ bool QQuickTextInputPrivate::isValidInput(QChar key, QChar mask) const
 
     Otherwise returns false
 */
-bool QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
+QQuickTextInputPrivate::ValidatorState QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
 {
 #ifndef QT_NO_VALIDATOR
     QString textCopy = str;
     int cursorCopy = m_cursor;
-    if (m_validator && m_validator->validate(textCopy, cursorCopy)
-        != QValidator::Acceptable)
-        return false;
+    if (m_validator) {
+        QValidator::State state = m_validator->validate(textCopy, cursorCopy);
+        if (state != QValidator::Acceptable)
+            return ValidatorState(state);
+    }
 #endif
 
     if (!m_maskData)
-        return true;
+        return AcceptableInput;
 
     if (str.length() != m_maxLength)
-        return false;
+        return InvalidInput;
 
     for (int i=0; i < m_maxLength; ++i) {
         if (m_maskData[i].separator) {
             if (str.at(i) != m_maskData[i].maskChar)
-                return false;
+                return InvalidInput;
         } else {
             if (!isValidInput(str.at(i), m_maskData[i].maskChar))
-                return false;
+                return InvalidInput;
         }
     }
-    return true;
+    return AcceptableInput;
 }
 
 /*!
@@ -3225,12 +3757,12 @@ QString QQuickTextInputPrivate::stripString(const QString &str) const
 
     QString s;
     int end = qMin(m_maxLength, (int)str.length());
-    for (int i = 0; i < end; ++i)
+    for (int i = 0; i < end; ++i) {
         if (m_maskData[i].separator)
             s += m_maskData[i].maskChar;
-        else
-            if (str[i] != m_blank)
-                s += str[i];
+        else if (str[i] != m_blank)
+            s += str[i];
+    }
 
     return s;
 }
@@ -3298,13 +3830,15 @@ void QQuickTextInputPrivate::internalUndo(int until)
         }
         if (until < 0 && m_undoState) {
             Command& next = m_history[m_undoState-1];
-            if (next.type != cmd.type && next.type < RemoveSelection
-                 && (cmd.type < RemoveSelection || next.type == Separator))
+            if (next.type != cmd.type
+                    && next.type < RemoveSelection
+                    && (cmd.type < RemoveSelection || next.type == Separator)) {
                 break;
+            }
         }
     }
+    separate();
     m_textDirty = true;
-    emitCursorPositionChanged();
 }
 
 void QQuickTextInputPrivate::internalRedo()
@@ -3341,13 +3875,30 @@ void QQuickTextInputPrivate::internalRedo()
         }
         if (m_undoState < (int)m_history.size()) {
             Command& next = m_history[m_undoState];
-            if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
-                 && (next.type < RemoveSelection || cmd.type == Separator))
+            if (next.type != cmd.type
+                    && cmd.type < RemoveSelection
+                    && next.type != Separator
+                    && (next.type < RemoveSelection || cmd.type == Separator)) {
                 break;
+            }
         }
     }
     m_textDirty = true;
-    emitCursorPositionChanged();
+}
+
+void QQuickTextInputPrivate::emitUndoRedoChanged()
+{
+    Q_Q(QQuickTextInput);
+    const bool previousUndo = canUndo;
+    const bool previousRedo = canRedo;
+
+    canUndo = isUndoAvailable();
+    canRedo = isRedoAvailable();
+
+    if (previousUndo != canUndo)
+        emit q->canUndoChanged();
+    if (previousRedo != canRedo)
+        emit q->canRedoChanged();
 }
 
 /*!
@@ -3356,7 +3907,7 @@ void QQuickTextInputPrivate::internalRedo()
     If the current cursor position differs from the last emitted cursor
     position, emits cursorPositionChanged().
 */
-void QQuickTextInputPrivate::emitCursorPositionChanged()
+bool QQuickTextInputPrivate::emitCursorPositionChanged()
 {
     Q_Q(QQuickTextInput);
     if (m_cursor != m_lastCursorPos) {
@@ -3364,10 +3915,6 @@ void QQuickTextInputPrivate::emitCursorPositionChanged()
 
         q->updateCursorRectangle();
         emit q->cursorPositionChanged();
-        // XXX todo - not in 4.8?
-    #if 0
-        resetCursorBlinkTimer();
-    #endif
 
         if (!hasSelectedText()) {
             if (lastSelectionStart != m_cursor) {
@@ -3381,9 +3928,13 @@ void QQuickTextInputPrivate::emitCursorPositionChanged()
         }
 
 #ifndef QT_NO_ACCESSIBILITY
-        QAccessible::updateAccessibility(q, 0, QAccessible::TextCaretMoved);
+        QAccessibleTextCursorEvent ev(q, m_cursor);
+        QAccessible::updateAccessibility(&ev);
 #endif
+
+        return true;
     }
+    return false;
 }
 
 
@@ -3400,53 +3951,37 @@ void QQuickTextInputPrivate::setCursorBlinkPeriod(int msec)
         m_blinkStatus = 1;
     } else {
         m_blinkTimer = 0;
-        if (m_blinkStatus == 1)
+        if (m_blinkStatus == 1) {
+            updateType = UpdatePaintNode;
             q->update();
+        }
     }
     m_blinkPeriod = msec;
 }
 
-void QQuickTextInputPrivate::resetCursorBlinkTimer()
-{
-    Q_Q(QQuickTextInput);
-    if (m_blinkPeriod == 0 || m_blinkTimer == 0)
-        return;
-    q->killTimer(m_blinkTimer);
-    m_blinkTimer = q->startTimer(m_blinkPeriod / 2);
-    m_blinkStatus = 1;
-}
-
 void QQuickTextInput::timerEvent(QTimerEvent *event)
 {
     Q_D(QQuickTextInput);
     if (event->timerId() == d->m_blinkTimer) {
         d->m_blinkStatus = !d->m_blinkStatus;
+        d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
-    } else if (event->timerId() == d->m_deleteAllTimer) {
-        killTimer(d->m_deleteAllTimer);
-        d->m_deleteAllTimer = 0;
-        d->clear();
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
     } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
         d->m_passwordEchoTimer.stop();
         d->updateDisplayText();
-#endif
+        updateCursorRectangle();
     }
 }
 
 void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
 {
     Q_Q(QQuickTextInput);
-    bool inlineCompletionAccepted = false;
 
     if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
         if (hasAcceptableInput(m_text) || fixup()) {
             emit q->accepted();
         }
-        if (inlineCompletionAccepted)
-            event->accept();
-        else
-            event->ignore();
+        event->ignore();
         return;
     }
 
@@ -3471,12 +4006,10 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
     }
 #ifndef QT_NO_SHORTCUT
     else if (event == QKeySequence::Undo) {
-        if (!m_readOnly)
-            undo();
+        q->undo();
     }
     else if (event == QKeySequence::Redo) {
-        if (!m_readOnly)
-            redo();
+        q->redo();
     }
     else if (event == QKeySequence::SelectAll) {
         selectAll();
@@ -3498,11 +4031,8 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
         }
     }
     else if (event == QKeySequence::DeleteEndOfLine) {
-        if (!m_readOnly) {
-            setSelection(m_cursor, end());
-            copy();
-            del();
-        }
+        if (!m_readOnly)
+            deleteEndOfLine();
     }
 #endif //QT_NO_CLIPBOARD
     else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
@@ -3567,16 +4097,12 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
             del();
     }
     else if (event == QKeySequence::DeleteEndOfWord) {
-        if (!m_readOnly) {
-            cursorWordForward(true);
-            del();
-        }
+        if (!m_readOnly)
+            deleteEndOfWord();
     }
     else if (event == QKeySequence::DeleteStartOfWord) {
-        if (!m_readOnly) {
-            cursorWordBackward(true);
-            del();
-        }
+        if (!m_readOnly)
+            deleteStartOfWord();
     }
 #endif // QT_NO_SHORTCUT
     else {
@@ -3584,10 +4110,8 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
         if (event->modifiers() & Qt::ControlModifier) {
             switch (event->key()) {
             case Qt::Key_Backspace:
-                if (!m_readOnly) {
-                    cursorWordBackward(true);
-                    del();
-                }
+                if (!m_readOnly)
+                    deleteStartOfWord();
                 break;
             default:
                 if (!handled)
@@ -3627,6 +4151,58 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
         event->accept();
 }
 
+/*!
+    \internal
+
+    Deletes the portion of the word before the current cursor position.
+*/
+
+void QQuickTextInputPrivate::deleteStartOfWord()
+{
+    int priorState = m_undoState;
+    Command cmd(SetSelection, m_cursor, 0, m_selstart, m_selend);
+    separate();
+    cursorWordBackward(true);
+    addCommand(cmd);
+    removeSelectedText();
+    finishChange(priorState);
+}
+
+/*!
+    \internal
+
+    Deletes the portion of the word after the current cursor position.
+*/
+
+void QQuickTextInputPrivate::deleteEndOfWord()
+{
+    int priorState = m_undoState;
+    Command cmd(SetSelection, m_cursor, 0, m_selstart, m_selend);
+    separate();
+    cursorWordForward(true);
+    // moveCursor (sometimes) calls separate() so we need to add the command after that so the
+    // cursor position and selection are restored in the same undo operation as the remove.
+    addCommand(cmd);
+    removeSelectedText();
+    finishChange(priorState);
+}
+
+/*!
+    \internal
+
+    Deletes all text from the cursor position to the end of the line.
+*/
+
+void QQuickTextInputPrivate::deleteEndOfLine()
+{
+    int priorState = m_undoState;
+    Command cmd(SetSelection, m_cursor, 0, m_selstart, m_selend);
+    separate();
+    setSelection(m_cursor, end());
+    addCommand(cmd);
+    removeSelectedText();
+    finishChange(priorState);
+}
 
 QT_END_NAMESPACE