Create new documentation structure
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextinput.cpp
index 5aa01d2..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>
@@ -66,9 +68,10 @@ DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
 /*!
     \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.
 
@@ -102,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);
 }
 
 /*!
@@ -467,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;
 }
@@ -637,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);
 }
 
@@ -806,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.
 
@@ -868,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.
 
@@ -960,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.
@@ -1215,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);
 }
 
 /*!
@@ -1382,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) {
@@ -1655,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) {
@@ -1728,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();
@@ -2210,6 +2201,7 @@ void QQuickTextInput::setPersistentSelection(bool on)
     emit persistentSelectionChanged();
 }
 
+#ifndef QT_NO_CLIPBOARD
 /*!
     \qmlproperty bool QtQuick2::TextInput::canPaste
 
@@ -2226,6 +2218,7 @@ bool QQuickTextInput::canPaste() const
     }
     return d->canPaste;
 }
+#endif
 
 /*!
     \qmlproperty bool QtQuick2::TextInput::canUndo
@@ -2263,7 +2256,7 @@ bool QQuickTextInput::canRedo() const
 qreal QQuickTextInput::contentWidth() const
 {
     Q_D(const QQuickTextInput);
-    return d->boundingRect.width();
+    return d->contentSize.width();
 }
 
 /*!
@@ -2276,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)
@@ -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;
 }
@@ -2725,7 +2744,6 @@ void QQuickTextInputPrivate::updateLayout()
     if (!q->isComponentComplete())
         return;
 
-    const QRectF previousRect = boundingRect;
 
     QTextOption option = m_textLayout.textOption();
     option.setTextDirection(layoutDirection());
@@ -2734,8 +2752,8 @@ void QQuickTextInputPrivate::updateLayout()
     m_textLayout.setTextOption(option);
     m_textLayout.setFont(font);
 
-    boundingRect = QRectF();
     m_textLayout.beginLayout();
+
     QTextLine line = m_textLayout.createLine();
     if (requireImplicitWidth) {
         line.setLineWidth(INT_MAX);
@@ -2748,12 +2766,14 @@ void QQuickTextInputPrivate::updateLayout()
     }
     qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
     qreal height = 0;
+    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();
@@ -2763,15 +2783,18 @@ void QQuickTextInputPrivate::updateLayout()
 
     textLayoutDirty = true;
 
+    const QSizeF previousSize = contentSize;
+    contentSize = QSizeF(width, height);
+
     updateType = UpdatePaintNode;
     q->update();
 
     if (!requireImplicitWidth && !q->widthValid())
-        q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
+        q->setImplicitSize(width, height);
     else
-        q->setImplicitHeight(qCeil(boundingRect.height()));
+        q->setImplicitHeight(height);
 
-    if (previousRect != boundingRect)
+    if (previousSize != contentSize)
         emit q->contentSizeChanged();
 }
 
@@ -3125,10 +3148,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);
@@ -3151,17 +3173,16 @@ 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();
     }
 
     if (isGettingInput)
         finishChange(priorState);
 
-    if (cursorVisible != oldCursorVisible)
-        emit q->cursorVisibleChanged(cursorVisible);
+    q->setCursorVisible(cursorVisible);
 
     if (selectionChange) {
         emit q->selectionChanged();
@@ -3268,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();
@@ -3975,6 +3996,7 @@ void QQuickTextInput::timerEvent(QTimerEvent *event)
     } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
         d->m_passwordEchoTimer.stop();
         d->updateDisplayText();
+        updateCursorRectangle();
     }
 }