Fix QQuickTextInput::moveCursorSelection()
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextinput.cpp
index e219934..22551c9 100644 (file)
@@ -1,38 +1,38 @@
 /****************************************************************************
 **
-** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
 **
 ** 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 <QtCore/qcoreapplication.h>
 #include <QtQml/qqmlinfo.h>
 #include <QtGui/qevent.h>
@@ -64,13 +66,15 @@ QT_BEGIN_NAMESPACE
 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
 
 /*!
-    \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,8 +106,8 @@ void QQuickTextInput::componentComplete()
     d->checkIsValid();
     d->updateLayout();
     updateCursorRectangle();
-    if (d->cursorComponent && d->cursorComponent->isReady())
-        createCursor();
+    if (d->cursorComponent && isCursorVisible())
+        QQuickTextUtil::createCursor(d);
 }
 
 /*!
@@ -130,6 +134,42 @@ void QQuickTextInput::setText(const QString &s)
     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
 
@@ -644,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
@@ -669,9 +709,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);
 }
 
@@ -747,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.
 
@@ -836,11 +880,13 @@ 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.
 
     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
@@ -898,11 +944,13 @@ void QQuickIntValidator::resetLocaleName()
 */
 
 /*!
-    \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
 
-    This element provides 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.
@@ -990,11 +1038,13 @@ void QQuickDoubleValidator::resetLocaleName()
 */
 
 /*!
-    \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.
 */
 /*!
@@ -1043,14 +1093,32 @@ 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->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()
@@ -1247,65 +1315,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);
 }
 
 /*!
@@ -1492,7 +1509,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
         forceActiveFocus();
         // re-open input panel on press if already focused
         if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
-            openSoftwareInputPanel();
+            qGuiApp->inputMethod()->show();
     }
 
     event->setAccepted(true);
@@ -1629,7 +1646,7 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
 {
     Q_D(QQuickTextInput);
     if (!d->inLayout) {
-        if (newGeometry.width() != oldGeometry.width() && d->wrapMode != NoWrap)
+        if (newGeometry.width() != oldGeometry.width())
             d->updateLayout();
         updateCursorRectangle();
     }
@@ -1692,18 +1709,8 @@ void QQuickTextInputPrivate::updateVerticalScroll()
 
     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 {
         QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + preeditLength);
         QRectF r = currentLine.isValid() ? currentLine.rect() : QRectF();
@@ -1765,13 +1772,14 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
         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());
 
@@ -1845,7 +1853,7 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::deselect()
+    \qmlmethod QtQuick2::TextInput::deselect()
 
     Removes active text selection.
 */
@@ -1856,7 +1864,7 @@ void QQuickTextInput::deselect()
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::selectAll()
+    \qmlmethod QtQuick2::TextInput::selectAll()
 
     Causes all text to be selected.
 */
@@ -1867,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.
@@ -1891,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();
+    }
 }
 
 /*!
@@ -1920,6 +1930,8 @@ void QQuickTextInput::paste()
 #endif // QT_NO_CLIPBOARD
 
 /*!
+    \qmlmethod QtQuick2::TextInput::undo()
+
     Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
     current selection, and updates the selection start to the current cursor
     position.
@@ -1935,6 +1947,8 @@ void QQuickTextInput::undo()
 }
 
 /*!
+    \qmlmethod QtQuick2::TextInput::redo()
+
     Redoes the last operation if redo is \l {canRedo}{available}.
 */
 
@@ -1948,7 +1962,7 @@ void QQuickTextInput::redo()
 }
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::insert(int position, string text)
+    \qmlmethod QtQuick2::TextInput::insert(int position, string text)
 
     Inserts \a text into the TextInput at position.
 */
@@ -2100,7 +2114,7 @@ void QQuickTextInput::remove(int start, int end)
 
 
 /*!
-    \qmlmethod void QtQuick2::TextInput::selectWord()
+    \qmlmethod QtQuick2::TextInput::selectWord()
 
     Causes the word closest to the current cursor position to be selected.
 */
@@ -2111,20 +2125,6 @@ void QQuickTextInput::selectWord()
 }
 
 /*!
-    \qmlproperty bool QtQuick2::TextInput::smooth
-
-    This property holds whether the text is smoothly scaled or transformed.
-
-    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.
-
-    \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.
-*/
-
-/*!
    \qmlproperty string QtQuick2::TextInput::passwordCharacter
 
    This is the character displayed when echoMode is set to Password or
@@ -2193,7 +2193,7 @@ void QQuickTextInput::setSelectByMouse(bool on)
 }
 
 /*!
-    \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode
+    \qmlproperty enumeration QtQuick2::TextInput::mouseSelectionMode
 
     Specifies how text should be selected using a mouse.
 
@@ -2320,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.)
@@ -2378,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;
@@ -2396,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);
@@ -2413,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 2.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->inputMethod()->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 2.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->inputMethod()->hide();
-}
-
 void QQuickTextInput::focusInEvent(QFocusEvent *event)
 {
     Q_D(const QQuickTextInput);
     if (d->focusOnPress && !d->m_readOnly)
-        openSoftwareInputPanel();
+        qGuiApp->inputMethod()->show();
     QQuickImplicitSizeItem::focusInEvent(event);
 }
 
@@ -2512,7 +2425,7 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
     Q_D(QQuickTextInput);
     if (change == ItemActiveFocusHasChanged) {
         bool hasFocus = value.boolValue;
-        setCursorVisible(hasFocus); // ### refactor:  && d->canvas && d->canvas->hasFocus()
+        setCursorVisible(hasFocus);
         if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) {
             d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
         }
@@ -2552,8 +2465,13 @@ bool QQuickTextInput::isInputMethodComposing() const
 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
@@ -2567,7 +2485,7 @@ void QQuickTextInputPrivate::init()
 
     if (!qmlDisableDistanceField()) {
         QTextOption option = m_textLayout.textOption();
-        option.setUseDesignMetrics(true);
+        option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
         m_textLayout.setTextOption(option);
     }
 }
@@ -2632,18 +2550,8 @@ QRectF QQuickTextInput::boundingRect() const
     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;
-        }
-    }
+    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());
@@ -2922,7 +2830,7 @@ void QQuickTextInputPrivate::cancelPreedit()
 void QQuickTextInputPrivate::backspace()
 {
     int priorState = m_undoState;
-    if (hasSelectedText()) {
+    if (separateSelection()) {
         removeSelectedText();
     } else if (m_cursor) {
             --m_cursor;
@@ -2955,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;
@@ -2975,7 +2883,8 @@ void QQuickTextInputPrivate::del()
 void QQuickTextInputPrivate::insert(const QString &newText)
 {
     int priorState = m_undoState;
-    removeSelectedText();
+    if (separateSelection())
+        removeSelectedText();
     internalInsert(newText);
     finishChange(priorState);
 }
@@ -2988,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();
@@ -3138,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;
@@ -3189,10 +3100,9 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
     m_textLayout.setPreeditArea(m_cursor, event->preeditString());
 #endif //QT_NO_IM
     const int oldPreeditCursor = m_preeditCursor;
-    const bool oldCursorVisible = cursorVisible;
     m_preeditCursor = event->preeditString().length();
     hasImState = !event->preeditString().isEmpty();
-    cursorVisible = true;
+    bool cursorVisible = true;
     QList<QTextLayout::FormatRange> formats;
     for (int i = 0; i < event->attributes().size(); ++i) {
         const QInputMethodEvent::Attribute &a = event->attributes().at(i);
@@ -3215,17 +3125,16 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
     m_textLayout.setAdditionalFormats(formats);
 
     updateDisplayText(/*force*/ true);
-    if (cursorPositionChanged) {
-        emitCursorPositionChanged();
-    } else if (m_preeditCursor != oldPreeditCursor || isGettingInput) {
+    if ((cursorPositionChanged && !emitCursorPositionChanged())
+            || m_preeditCursor != oldPreeditCursor
+            || isGettingInput) {
         q->updateCursorRectangle();
     }
 
     if (isGettingInput)
         finishChange(priorState);
 
-    if (cursorVisible != oldCursorVisible)
-        emit q->cursorVisibleChanged(cursorVisible);
+    q->setCursorVisible(cursorVisible);
 
     if (selectionChange) {
         emit q->selectionChanged();
@@ -3412,8 +3321,7 @@ void QQuickTextInputPrivate::internalInsert(const QString &s)
         if (delay > 0)
             m_passwordEchoTimer.start(delay, q);
     }
-    if (hasSelectedText())
-        addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
+    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) {
@@ -3450,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) {
@@ -3477,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
@@ -3508,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.
 */
@@ -3906,11 +3830,14 @@ 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;
 }
 
@@ -3948,9 +3875,12 @@ 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;
@@ -4039,22 +3969,19 @@ void QQuickTextInput::timerEvent(QTimerEvent *event)
     } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
         d->m_passwordEchoTimer.stop();
         d->updateDisplayText();
+        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;
     }
 
@@ -4104,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) {
@@ -4173,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 {
@@ -4190,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)
@@ -4233,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