Create new documentation structure
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextinput.cpp
index 3b62e99..a8d10f3 100644 (file)
 #include "qquicktextinput_p.h"
 #include "qquicktextinput_p_p.h"
 #include "qquickcanvas.h"
+#include "qquicktextutil_p.h"
 
 #include <private/qqmlglobal_p.h>
 
+
+#include <QtCore/qcoreapplication.h>
 #include <QtQml/qqmlinfo.h>
 #include <QtGui/qevent.h>
 #include <QTextBoundaryFinder>
@@ -62,16 +65,13 @@ 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
     \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.
 
@@ -105,8 +105,8 @@ void QQuickTextInput::componentComplete()
     d->checkIsValid();
     d->updateLayout();
     updateCursorRectangle();
-    if (d->cursorComponent && d->cursorComponent->isReady())
-        createCursor();
+    if (d->cursorComponent && isCursorVisible())
+        QQuickTextUtil::createCursor(d);
 }
 
 /*!
@@ -119,8 +119,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);
 }
@@ -130,9 +128,8 @@ void QQuickTextInput::setText(const QString &s)
     Q_D(QQuickTextInput);
     if (s == text())
         return;
-    if (d->composeMode())
-        qApp->inputMethod()->reset();
-    d->m_tentativeCommit.clear();
+
+    d->cancelPreedit();
     d->internalSetText(s, -1, false);
 }
 
@@ -201,11 +198,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
@@ -271,11 +268,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
@@ -473,16 +470,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() ? qApp->inputMethod()->inputDirection() == 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;
 }
@@ -512,10 +541,10 @@ void QQuickTextInput::setVAlign(QQuickTextInput::VAlignment alignment)
     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.
@@ -643,9 +672,13 @@ void QQuickTextInput::setCursorVisible(bool on)
     if (d->cursorVisible == on)
         return;
     d->cursorVisible = on;
-    d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
-    d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
-    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);
 }
 
@@ -681,9 +714,7 @@ 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);
@@ -814,7 +845,8 @@ void QQuickTextInput::setAutoScroll(bool b)
 /*!
     \qmlclass IntValidator 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.
 
@@ -876,7 +908,8 @@ void QQuickIntValidator::resetLocaleName()
 /*!
     \qmlclass DoubleValidator QDoubleValidator
     \inqmlmodule QtQuick 2
-    \ingroup qml-basic-visual-elements
+    \ingroup qtquick-text-utility
+    \brief Defines a validator for non-integer numbers
 
     This element provides a validator for non-integer numbers.
 
@@ -958,8 +991,8 @@ void QQuickDoubleValidator::resetLocaleName()
     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).
@@ -968,7 +1001,8 @@ void QQuickDoubleValidator::resetLocaleName()
 /*!
     \qmlclass RegExpValidator 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
     matches a specified regular expression.
@@ -1110,10 +1144,10 @@ Qt::InputMethodHints QQuickTextInputPrivate::effectiveInputMethodHints() const
 
     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
 */
@@ -1149,37 +1183,37 @@ void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
     Flags that alter behaviour are:
 
     \list
-    \o Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
+    \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.
-    \o Qt.ImhSensitiveData - Typed text should not be stored by the active input method
+    \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
             in any persistent storage like predictive user dictionary.
-    \o Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
+    \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
             when a sentence ends.
-    \o Qt.ImhPreferNumbers - Numbers are preferred (but not required).
-    \o Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
-    \o Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
-    \o Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
+    \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.
 
-    \o Qt.ImhDate - The text editor functions as a date field.
-    \o Qt.ImhTime - The text editor functions as a time field.
+    \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
-    \o Qt.ImhDigitsOnly - Only digits are allowed.
-    \o Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
-    \o Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
-    \o Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
-    \o Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
-    \o Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
-    \o Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
+    \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
-    \o Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
+    \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
     \endlist
 */
 
@@ -1223,65 +1257,14 @@ QQmlComponent* QQuickTextInput::cursorDelegate() const
 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;
-        d->cursorItem = 0;
-    } 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;
-    QQmlContext *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();
-
-    QQml_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);
 }
 
 /*!
@@ -1323,8 +1306,8 @@ 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
 */
 
@@ -1390,7 +1373,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) {
@@ -1405,7 +1388,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 {
@@ -1414,7 +1397,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();
 }
 
@@ -1444,13 +1427,9 @@ 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;
@@ -1463,12 +1442,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)
+            openSoftwareInputPanel();
+    }
+
     event->setAccepted(true);
 }
 
@@ -1602,9 +1587,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->wrapMode != NoWrap)
+            d->updateLayout();
+        updateCursorRectangle();
+    }
     QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
 }
 
@@ -1614,14 +1601,19 @@ void QQuickTextInputPrivate::updateHorizontalScroll()
     QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor);
     const int preeditLength = m_textLayout.preeditAreaText().length();
     const qreal width = qMax<qreal>(0, q->width());
-    qreal widthUsed = currentLine.isValid() ? currentLine.naturalTextWidth() : 0;
+    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 {
         Q_ASSERT(currentLine.isValid());
-        qreal cix = currentLine.cursorToX(m_cursor + preeditLength);
         if (cix - hscroll >= width) {
             // text doesn't fit, cursor is to the right of br (scroll right)
             hscroll = cix - width;
@@ -1632,6 +1624,10 @@ 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
@@ -1650,7 +1646,7 @@ void QQuickTextInputPrivate::updateVerticalScroll()
     Q_Q(QQuickTextInput);
     const int preeditLength = m_textLayout.preeditAreaText().length();
     const qreal height = qMax<qreal>(0, q->height());
-    qreal heightUsed = boundingRect.height();
+    qreal heightUsed = contentSize.height();
     qreal previousScroll = vscroll;
 
     if (!autoScroll || heightUsed <=  height) {
@@ -1723,12 +1719,12 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
         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();
@@ -1919,11 +1915,11 @@ void QQuickTextInput::redo()
 void QQuickTextInput::insert(int position, const QString &text)
 {
     Q_D(QQuickTextInput);
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
-    if (d->m_echoMode == QQuickTextInput::Password)
-        d->m_passwordEchoTimer.start(qt_passwordEchoDelay, this);
-#endif
-
+    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;
 
@@ -2161,8 +2157,8 @@ void QQuickTextInput::setSelectByMouse(bool on)
     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.
@@ -2205,6 +2201,7 @@ void QQuickTextInput::setPersistentSelection(bool on)
     emit persistentSelectionChanged();
 }
 
+#ifndef QT_NO_CLIPBOARD
 /*!
     \qmlproperty bool QtQuick2::TextInput::canPaste
 
@@ -2221,6 +2218,7 @@ bool QQuickTextInput::canPaste() const
     }
     return d->canPaste;
 }
+#endif
 
 /*!
     \qmlproperty bool QtQuick2::TextInput::canUndo
@@ -2258,7 +2256,7 @@ bool QQuickTextInput::canRedo() const
 qreal QQuickTextInput::contentWidth() const
 {
     Q_D(const QQuickTextInput);
-    return d->boundingRect.width();
+    return d->contentSize.width();
 }
 
 /*!
@@ -2271,7 +2269,7 @@ qreal QQuickTextInput::contentWidth() const
 qreal QQuickTextInput::contentHeight() const
 {
     Q_D(const QQuickTextInput);
-    return d->boundingRect.height();
+    return d->contentSize.height();
 }
 
 void QQuickTextInput::moveCursorSelection(int position)
@@ -2295,9 +2293,9 @@ void QQuickTextInput::moveCursorSelection(int position)
     basis.  If not specified the selection mode will default to TextInput.SelectCharacters.
 
     \list
-    \o TextInput.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 TextInput.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
@@ -2474,16 +2472,11 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
     if (change == ItemActiveFocusHasChanged) {
         bool hasFocus = value.boolValue;
         setCursorVisible(hasFocus); // ### refactor:  && d->canvas && d->canvas->hasFocus()
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
         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();
             if (!d->persistentSelection)
                 d->deselect();
             disconnect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
@@ -2512,7 +2505,7 @@ 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()
@@ -2595,10 +2588,36 @@ QRectF QQuickTextInput::boundingRect() const
 {
     Q_D(const QQuickTextInput);
 
+    int cursorWidth = d->cursorItem ? 0 : 1;
+
+    qreal hscroll = d->hscroll;
+    if (!d->autoScroll || d->contentSize.width() < width()) {
+        switch (effectiveHAlign()) {
+        case AlignLeft:
+            break;
+        case AlignRight:
+            hscroll += d->contentSize.width() - width();
+            break;
+        case AlignHCenter:
+            hscroll += (d->contentSize.width() - width()) / 2;
+            break;
+        }
+    }
+
+    // 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::boundingRect();
+    QRectF r = QQuickImplicitSizeItem::clipRect();
     r.setRight(r.right() + cursorWidth);
     return r;
 }
@@ -2651,7 +2670,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);
@@ -2664,7 +2682,6 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
                     str[cursor - 1] = uc;
             }
         }
-#endif
     } else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
         str.fill(m_passwordCharacter);
     }
@@ -2688,6 +2705,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);
@@ -2695,28 +2744,36 @@ void QQuickTextInputPrivate::updateLayout()
     if (!q->isComponentComplete())
         return;
 
-    const QRectF previousRect = boundingRect;
 
     QTextOption option = m_textLayout.textOption();
     option.setTextDirection(layoutDirection());
-    option.setFlags(QTextOption::IncludeTrailingSpaces);
     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();
@@ -2726,11 +2783,18 @@ void QQuickTextInputPrivate::updateLayout()
 
     textLayoutDirty = true;
 
+    const QSizeF previousSize = contentSize;
+    contentSize = QSizeF(width, height);
+
     updateType = UpdatePaintNode;
     q->update();
-    q->setImplicitSize(boundingRect.width(), boundingRect.height());
 
-    if (previousRect != boundingRect)
+    if (!requireImplicitWidth && !q->widthValid())
+        q->setImplicitSize(width, height);
+    else
+        q->setImplicitHeight(height);
+
+    if (previousSize != contentSize)
         emit q->contentSizeChanged();
 }
 
@@ -2775,26 +2839,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->inputMethod()->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);
+
+    if (!hasImState)
+        return;
+
+    qApp->inputMethod()->reset();
 
-    m_preeditCursor = 0;
-    m_textLayout.setPreeditArea(-1, QString());
-    m_textLayout.clearAdditionalFormats();
-    updateLayout();
+    QInputMethodEvent ev;
+    QCoreApplication::sendEvent(q, &ev);
 }
 
 /*!
@@ -3077,14 +3149,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;
@@ -3098,22 +3173,17 @@ 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);
 
+    q->setCursorVisible(cursorVisible);
+
     if (selectionChange) {
         emit q->selectionChanged();
         q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
@@ -3181,15 +3251,6 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
                     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
@@ -3228,7 +3289,7 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
         emit q->selectionChanged();
     }
 
-    inputMethodAttributesChanged |= (m_cursor == m_lastCursorPos);
+    inputMethodAttributesChanged |= (m_cursor != m_lastCursorPos);
     if (inputMethodAttributesChanged)
         q->updateInputMethod();
     emitUndoRedoChanged();
@@ -3264,8 +3325,10 @@ void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool e
 #ifdef QT_NO_ACCESSIBILITY
     Q_UNUSED(changed)
 #else
-    if (changed)
-        QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::TextUpdated, q));
+    if (changed) {
+        QAccessibleTextUpdateEvent ev(q, 0, oldText, m_text);
+        QAccessible::updateAccessibility(&ev);
+    }
 #endif
 }
 
@@ -3300,11 +3363,12 @@ 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 (m_echoMode == QQuickTextInput::Password) {
+        int delay = qGuiApp->styleHints()->passwordMaskDelay();
+        if (delay > 0)
+            m_passwordEchoTimer.start(delay, q);
+    }
     if (hasSelectedText())
         addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
     if (m_maskData) {
@@ -3891,7 +3955,8 @@ bool QQuickTextInputPrivate::emitCursorPositionChanged()
         }
 
 #ifndef QT_NO_ACCESSIBILITY
-        QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::TextCaretMoved, q));
+        QAccessibleTextCursorEvent ev(q, m_cursor);
+        QAccessible::updateAccessibility(&ev);
 #endif
 
         return true;
@@ -3928,11 +3993,10 @@ void QQuickTextInput::timerEvent(QTimerEvent *event)
         d->m_blinkStatus = !d->m_blinkStatus;
         d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
         update();
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
     } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
         d->m_passwordEchoTimer.stop();
         d->updateDisplayText();
-#endif
+        updateCursorRectangle();
     }
 }
 
@@ -3973,12 +4037,10 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
     }
 #ifndef QT_NO_SHORTCUT
     else if (event == QKeySequence::Undo) {
-        if (!m_readOnly)
-            q->undo();
+        q->undo();
     }
     else if (event == QKeySequence::Redo) {
-        if (!m_readOnly)
-            q->redo();
+        q->redo();
     }
     else if (event == QKeySequence::SelectAll) {
         selectAll();