X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fquick%2Fitems%2Fqquicktextinput.cpp;h=22551c9d4c2e97c94fd907e968182991d5185eaf;hb=5c4108e2f0bcd8c116882eeb4aa96dc02cd22c06;hp=e2199347e7295f59d8b1008636b46a9b8f924d93;hpb=bf817782e6dfeb8455febe3e481bcdb4c383ed05;p=profile%2Fivi%2Fqtdeclarative.git diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index e219934..22551c9 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -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$ @@ -41,10 +41,12 @@ #include "qquicktextinput_p.h" #include "qquicktextinput_p_p.h" -#include "qquickcanvas.h" +#include "qquickwindow.h" +#include "qquicktextutil_p.h" #include + #include #include #include @@ -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(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 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