/****************************************************************************
**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
**
-** This file is part of the QtDeclarative module of the Qt Toolkit.
+** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
**
**
**
+**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qquicktextinput_p.h"
#include "qquicktextinput_p_p.h"
-#include "qquickcanvas.h"
+#include "qquickwindow.h"
+#include "qquicktextutil_p.h"
+
+#include <private/qqmlglobal_p.h>
-#include <private/qdeclarativeglobal_p.h>
-#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtQml/qqmlinfo.h>
#include <QtGui/qevent.h>
#include <QTextBoundaryFinder>
#include "qquicktextnode_p.h"
#include <QtQuick/qsgsimplerectnode.h>
#include <QtGui/qstylehints.h>
-#include <QtGui/qinputpanel.h>
+#include <QtGui/qinputmethod.h>
#ifndef QT_NO_ACCESSIBILITY
#include "qaccessible.h"
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.
+ 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),
QQuickImplicitSizeItem::componentComplete();
+ d->checkIsValid();
d->updateLayout();
updateCursorRectangle();
- if (d->cursorComponent && d->cursorComponent->isReady())
- createCursor();
+ if (d->cursorComponent && isCursorVisible())
+ QQuickTextUtil::createCursor(d);
}
/*!
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);
}
Q_D(QQuickTextInput);
if (s == text())
return;
- if (d->composeMode())
- qApp->inputPanel()->reset();
- d->m_tentativeCommit.clear();
+
+ d->cancelPreedit();
d->internalSetText(s, -1, false);
}
+
+/*!
+ \qmlproperty enumeration QtQuick2::TextInput::renderType
+
+ Override the default rendering type for this component.
+
+ Supported render types are:
+ \list
+ \li Text.QtRendering - the default
+ \li Text.NativeRendering
+ \endlist
+
+ Select Text.NativeRendering if you prefer text to look native on the target platform and do
+ not require advanced features such as transformation of the text. Using such features in
+ combination with the NativeRendering render type will lend poor and sometimes pixelated
+ results.
+*/
+QQuickTextInput::RenderType QQuickTextInput::renderType() const
+{
+ Q_D(const QQuickTextInput);
+ return d->renderType;
+}
+
+void QQuickTextInput::setRenderType(QQuickTextInput::RenderType renderType)
+{
+ Q_D(QQuickTextInput);
+ if (d->renderType == renderType)
+ return;
+
+ d->renderType = renderType;
+ emit renderTypeChanged();
+
+ if (isComponentComplete())
+ d->updateLayout();
+}
+
+/*!
+ \qmlproperty int QtQuick2::TextInput::length
+
+ Returns the total number of characters in the TextInput item.
+
+ If the TextInput has an inputMask the length will include mask characters and may differ
+ from the length of the string returned by the \l text property.
+
+ This property can be faster than querying the length the \l text property as it doesn't
+ require any copying or conversion of the TextInput's internal string data.
+*/
+
+int QQuickTextInput::length() const
+{
+ Q_D(const QQuickTextInput);
+ return d->m_text.length();
+}
+
+/*!
+ \qmlmethod string QtQuick2::TextInput::getText(int start, int end)
+
+ Returns the section of text that is between the \a start and \a end positions.
+
+ If the TextInput has an inputMask the length will include mask characters.
+*/
+
+QString QQuickTextInput::getText(int start, int end) const
+{
+ Q_D(const QQuickTextInput);
+
+ if (start > end)
+ qSwap(start, end);
+
+ return d->m_text.mid(start, end - start);
+}
+
QString QQuickTextInputPrivate::realText() const
{
QString res = m_maskData ? stripString(m_text) : m_text;
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
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
if (oldFont != d->font) {
d->updateLayout();
updateCursorRectangle();
+ updateInputMethod(Qt::ImCursorRectangle | Qt::ImFont);
}
emit fontChanged(d->sourceFont);
}
if (c != d->color) {
d->color = c;
d->textLayoutDirty = true;
+ d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
update();
- emit colorChanged(c);
+ emit colorChanged();
}
}
return;
d->selectionColor = color;
- d->m_palette.setColor(QPalette::Highlight, d->selectionColor);
if (d->hasSelectedText()) {
d->textLayoutDirty = true;
+ d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
update();
}
- emit selectionColorChanged(color);
+ emit selectionColorChanged();
}
/*!
\qmlproperty color QtQuick2::TextInput::selectedTextColor
return;
d->selectedTextColor = color;
- d->m_palette.setColor(QPalette::HighlightedText, d->selectedTextColor);
if (d->hasSelectedText()) {
d->textLayoutDirty = true;
+ d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
update();
}
- emit selectedTextColorChanged(color);
+ emit selectedTextColorChanged();
}
/*!
The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and
\c TextInput.AlignHCenter.
- Valid values for \c verticalAlignment are \c TextEdit.AlignTop (default),
- \c TextEdit.AlignBottom \c TextEdit.AlignVCenter.
+ Valid values for \c verticalAlignment are \c TextInput.AlignTop (default),
+ \c TextInput.AlignBottom \c TextInput.AlignVCenter.
When using the attached property LayoutMirroring::enabled to mirror application
layouts, the horizontal alignment of text will also be mirrored. However, the property
return false;
}
+Qt::LayoutDirection QQuickTextInputPrivate::textDirection() const
+{
+ QString text = m_text;
+ if (text.isEmpty())
+ text = m_textLayout.preeditAreaText();
+
+ const QChar *character = text.constData();
+ while (!character->isNull()) {
+ switch (character->direction()) {
+ case QChar::DirL:
+ return Qt::LeftToRight;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirAN:
+ return Qt::RightToLeft;
+ default:
+ break;
+ }
+ character++;
+ }
+ return Qt::LayoutDirectionAuto;
+}
+
+Qt::LayoutDirection QQuickTextInputPrivate::layoutDirection() const
+{
+ Qt::LayoutDirection direction = m_layoutDirection;
+ if (direction == Qt::LayoutDirectionAuto) {
+ direction = textDirection();
+ if (direction == Qt::LayoutDirectionAuto)
+ direction = qApp->inputMethod()->inputDirection();
+ }
+ return (direction == Qt::LayoutDirectionAuto) ? Qt::LeftToRight : direction;
+}
+
bool QQuickTextInputPrivate::determineHorizontalAlignment()
{
if (hAlignImplicit) {
// if no explicit alignment has been set, follow the natural layout direction of the text
- QString text = q_func()->text();
- if (text.isEmpty())
- text = m_textLayout.preeditAreaText();
- bool isRightToLeft = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
- return setHAlign(isRightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
+ Qt::LayoutDirection direction = textDirection();
+ if (direction == Qt::LayoutDirectionAuto)
+ direction = qApp->inputMethod()->inputDirection();
+ return setHAlign(direction == Qt::RightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
}
return false;
}
/*!
\qmlproperty enumeration QtQuick2::TextInput::wrapMode
- Set this property to wrap the text to the TextEdit item's width.
+ Set this property to wrap the text to the TextInput item's width.
The text will only wrap if an explicit width has been set.
\list
- \o TextInput.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
- \o TextInput.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
- \o TextInput.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
- \o TextInput.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
+ \li TextInput.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
+ \li TextInput.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
+ \li TextInput.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \li TextInput.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
\endlist
The default is TextInput.NoWrap. If you set a width, consider using TextInput.Wrap.
d->m_readOnly = ro;
if (!ro)
d->setCursorPosition(d->end());
-
+ updateInputMethod(Qt::ImEnabled);
+ q_canPasteChanged();
+ d->emitUndoRedoChanged();
emit readOnlyChanged(ro);
}
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
if (d->cursorVisible == on)
return;
d->cursorVisible = on;
- d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
- update();
+ if (on && isComponentComplete())
+ QQuickTextUtil::createCursor(d);
+ if (!d->cursorItem) {
+ d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
+ d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
+ update();
+ }
emit cursorVisibleChanged(d->cursorVisible);
}
}
/*!
- Returns a Rect which encompasses the cursor, but which may be larger than is
- required. Ignores custom cursor delegates.
+ \qmlproperty rectangle QtQuick2::TextInput::cursorRectangle
+
+ The rectangle where the standard text cursor is rendered within the text input. Read only.
+
+ The position and height of a custom cursorDelegate are updated to follow the cursorRectangle
+ automatically when it changes. The width of the delegate is unaffected by changes in the
+ cursor rectangle.
*/
-QRect QQuickTextInput::cursorRectangle() const
+
+QRectF QQuickTextInput::cursorRectangle() const
{
Q_D(const QQuickTextInput);
- int c = d->m_cursor;
- if (d->m_preeditCursor != -1)
- c += d->m_preeditCursor;
+ int c = d->m_cursor + d->m_preeditCursor;
if (d->m_echoMode == NoEcho)
c = 0;
QTextLine l = d->m_textLayout.lineForTextPosition(c);
if (!l.isValid())
- return QRect();
- return QRect(
- qRound(l.cursorToX(c) - d->hscroll),
- qRound(l.y() - d->vscroll),
- d->m_cursorWidth,
- qCeil(l.height()));
+ return QRectF();
+ return QRectF(l.cursorToX(c) - d->hscroll, l.y() - d->vscroll, 1, l.height());
}
/*!
void QQuickTextInput::select(int start, int end)
{
Q_D(QQuickTextInput);
- if (start < 0 || end < 0 || start > text().length() || end > text().length())
+ if (start < 0 || end < 0 || start > d->m_text.length() || end > d->m_text.length())
return;
d->setSelection(start, end-start);
}
/*!
\qmlclass IntValidator QIntValidator
\inqmlmodule QtQuick 2
- \ingroup qml-basic-visual-elements
+ \ingroup qtquick-text-utility
+ \brief Defines 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
+ and negative signs. In addition, IntValidator is always guaranteed to accept a number
+ formatted according to the "C" locale.
+*/
+
+
+QQuickIntValidator::QQuickIntValidator(QObject *parent)
+ : QIntValidator(parent)
+{
+}
+
+/*!
+ \qmlproperty string QtQuick2::IntValidator::locale
- This element provides a validator for integer values.
+ This property holds the name of the locale used to interpret the number.
- IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and
- will accept locale specific digits, group separators, and positive and negative signs. In
- addition, IntValidator is always guaranteed to accept a number formatted according to the "C"
- locale.
+ \sa QML:Qt::locale()
*/
+
+QString QQuickIntValidator::localeName() const
+{
+ return locale().name();
+}
+
+void QQuickIntValidator::setLocaleName(const QString &name)
+{
+ if (locale().name() != name) {
+ setLocale(QLocale(name));
+ emit localeNameChanged();
+ }
+}
+
+void QQuickIntValidator::resetLocaleName()
+{
+ QLocale defaultLocale;
+ if (locale() != defaultLocale) {
+ setLocale(defaultLocale);
+ emit localeNameChanged();
+ }
+}
+
/*!
\qmlproperty int QtQuick2::IntValidator::top
/*!
\qmlclass DoubleValidator QDoubleValidator
\inqmlmodule QtQuick 2
- \ingroup qml-basic-visual-elements
+ \ingroup qtquick-text-utility
+ \brief Defines a validator for non-integer numbers
+
+ The DoubleValidator type provides a validator for non-integer numbers.
+
+ Input is accepted if it contains a double that is within the valid range
+ and is in the correct format.
+
+ Input is accepected but invalid if it contains a double that is outside
+ the range or is in the wrong format; e.g. with too many digits after the
+ decimal point or is empty.
- This element provides a validator for non-integer numbers.
+ Input is rejected if it is not a double.
+
+ Note: If the valid range consists of just positive doubles (e.g. 0.0 to
+ 100.0) and input is a negative double then it is rejected. If \l notation
+ is set to DoubleValidator.StandardNotation, and the input contains more
+ digits before the decimal point than a double in the valid range may have,
+ it is also rejected. If \l notation is DoubleValidator.ScientificNotation,
+ and the input is not in the valid range, it is accecpted but invalid. The
+ value may yet become valid by changing the exponent.
*/
+QQuickDoubleValidator::QQuickDoubleValidator(QObject *parent)
+ : QDoubleValidator(parent)
+{
+}
+
+/*!
+ \qmlproperty string QtQuick2::DoubleValidator::locale
+
+ This property holds the name of the locale used to interpret the number.
+
+ \sa QML:Qt::locale()
+*/
+
+QString QQuickDoubleValidator::localeName() const
+{
+ return locale().name();
+}
+
+void QQuickDoubleValidator::setLocaleName(const QString &name)
+{
+ if (locale().name() != name) {
+ setLocale(QLocale(name));
+ emit localeNameChanged();
+ }
+}
+
+void QQuickDoubleValidator::resetLocaleName()
+{
+ QLocale defaultLocale;
+ if (locale() != defaultLocale) {
+ setLocale(defaultLocale);
+ emit localeNameChanged();
+ }
+}
+
/*!
\qmlproperty real QtQuick2::DoubleValidator::top
The possible values for this property are:
\list
- \o DoubleValidator.StandardNotation
- \o DoubleValidator.ScientificNotation (default)
+ \li DoubleValidator.StandardNotation
+ \li DoubleValidator.ScientificNotation (default)
\endlist
If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2).
/*!
\qmlclass RegExpValidator QRegExpValidator
\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.
*/
/*!
input of integers between 11 and 31 into the text input:
\code
- import QtQuick 1.0
+ import QtQuick 2.0
TextInput{
validator: IntValidator{bottom: 11; top: 31;}
focus: true
if (d->m_validator == v)
return;
+ if (d->m_validator) {
+ qmlobject_disconnect(
+ d->m_validator, QValidator, SIGNAL(changed()),
+ this, QQuickTextInput, SLOT(q_validatorChanged()));
+ }
+
d->m_validator = v;
- if (!d->hasAcceptableInput(d->m_text)) {
- d->oldValidity = false;
- emit acceptableInputChanged();
+
+ if (d->m_validator) {
+ qmlobject_connect(
+ d->m_validator, QValidator, SIGNAL(changed()),
+ this, QQuickTextInput, SLOT(q_validatorChanged()));
}
+ if (isComponentComplete())
+ d->checkIsValid();
+
emit validatorChanged();
}
+
+void QQuickTextInput::q_validatorChanged()
+{
+ Q_D(QQuickTextInput);
+ d->checkIsValid();
+}
+
#endif // QT_NO_VALIDATOR
+void QQuickTextInputPrivate::checkIsValid()
+{
+ Q_Q(QQuickTextInput);
+
+ ValidatorState state = hasAcceptableInput(m_text);
+ m_validInput = state != InvalidInput;
+ if (state != AcceptableInput) {
+ if (m_acceptableInput) {
+ m_acceptableInput = false;
+ emit q->acceptableInputChanged();
+ }
+ } else if (!m_acceptableInput) {
+ m_acceptableInput = true;
+ emit q->acceptableInputChanged();
+ }
+}
+
/*!
\qmlproperty string QtQuick2::TextInput::inputMask
bool QQuickTextInput::hasAcceptableInput() const
{
Q_D(const QQuickTextInput);
- return d->hasAcceptableInput(d->m_text);
+ return d->hasAcceptableInput(d->m_text) == QQuickTextInputPrivate::AcceptableInput;
}
/*!
state.
*/
-void QQuickTextInputPrivate::updateInputMethodHints()
+Qt::InputMethodHints QQuickTextInputPrivate::effectiveInputMethodHints() const
{
- Q_Q(QQuickTextInput);
Qt::InputMethodHints hints = inputMethodHints;
if (m_echoMode == QQuickTextInput::Password || m_echoMode == QQuickTextInput::NoEcho)
hints |= Qt::ImhHiddenText;
else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
hints &= ~Qt::ImhHiddenText;
if (m_echoMode != QQuickTextInput::Normal)
- hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
- q->setInputMethodHints(hints);
+ hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
+ return hints;
}
/*!
\qmlproperty enumeration QtQuick2::TextInput::echoMode
Specifies how the text should be displayed in the TextInput.
\list
- \o TextInput.Normal - Displays the text as it is. (Default)
- \o TextInput.Password - Displays asterisks instead of characters.
- \o TextInput.NoEcho - Displays nothing.
- \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered
+ \li TextInput.Normal - Displays the text as it is. (Default)
+ \li TextInput.Password - Displays asterisks instead of characters.
+ \li TextInput.NoEcho - Displays nothing.
+ \li TextInput.PasswordEchoOnEdit - Displays characters as they are entered
while editing, otherwise displays asterisks.
\endlist
*/
d->cancelPasswordEchoTimer();
d->m_echoMode = echo;
d->m_passwordEchoEditing = false;
- d->updateInputMethodHints();
+ updateInputMethod(Qt::ImHints);
d->updateDisplayText();
updateCursorRectangle();
emit echoModeChanged(echoMode());
}
-Qt::InputMethodHints QQuickTextInput::imHints() const
+/*!
+ \qmlproperty enumeration QtQuick2::TextInput::inputMethodHints
+
+ Provides hints to the input method about the expected content of the text input and how it
+ should operate.
+
+ The value is a bit-wise combination of flags, or Qt.ImhNone if no hints are set.
+
+ Flags that alter behaviour are:
+
+ \list
+ \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
+ This is automatically set when setting echoMode to \c TextInput.Password.
+ \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
+ in any persistent storage like predictive user dictionary.
+ \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
+ when a sentence ends.
+ \li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
+ \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
+ \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
+ \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
+
+ \li Qt.ImhDate - The text editor functions as a date field.
+ \li Qt.ImhTime - The text editor functions as a time field.
+ \endlist
+
+ Flags that restrict input (exclusive flags) are:
+
+ \list
+ \li Qt.ImhDigitsOnly - Only digits are allowed.
+ \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
+ \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
+ \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
+ \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
+ \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
+ \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
+ \endlist
+
+ Masks:
+
+ \list
+ \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
+ \endlist
+*/
+
+Qt::InputMethodHints QQuickTextInput::inputMethodHints() const
{
Q_D(const QQuickTextInput);
return d->inputMethodHints;
}
-void QQuickTextInput::setIMHints(Qt::InputMethodHints hints)
+void QQuickTextInput::setInputMethodHints(Qt::InputMethodHints hints)
{
Q_D(QQuickTextInput);
- if (d->inputMethodHints == hints)
+
+ if (hints == d->inputMethodHints)
return;
+
d->inputMethodHints = hints;
- d->updateInputMethodHints();
+ updateInputMethod(Qt::ImHints);
+ emit inputMethodHintsChanged();
}
/*!
needed, and the x property of delegate instance will be set so as
to be one pixel before the top left of the current character.
- Note that the root item of the delegate component must be a QDeclarativeItem or
- QDeclarativeItem derived item.
+ Note that the root item of the delegate component must be a QQuickItem or
+ QQuickItem derived item.
*/
-QDeclarativeComponent* QQuickTextInput::cursorDelegate() const
+QQmlComponent* QQuickTextInput::cursorDelegate() const
{
Q_D(const QQuickTextInput);
return d->cursorComponent;
}
-void QQuickTextInput::setCursorDelegate(QDeclarativeComponent* c)
+void QQuickTextInput::setCursorDelegate(QQmlComponent* c)
{
Q_D(QQuickTextInput);
- if (d->cursorComponent == c)
- return;
-
- d->cursorComponent = c;
- if (!c) {
- //note that the components are owned by something else
- delete d->cursorItem;
- } else {
- d->startCreatingCursor();
- }
-
- emit cursorDelegateChanged();
-}
-
-void QQuickTextInputPrivate::startCreatingCursor()
-{
- Q_Q(QQuickTextInput);
- if (cursorComponent->isReady()) {
- q->createCursor();
- } else if (cursorComponent->isLoading()) {
- q->connect(cursorComponent, SIGNAL(statusChanged(int)),
- q, SLOT(createCursor()));
- } else { // isError
- qmlInfo(q, cursorComponent->errors()) << QQuickTextInput::tr("Could not load cursor delegate");
- }
+ QQuickTextUtil::setCursorDelegate(d, c);
}
void QQuickTextInput::createCursor()
{
Q_D(QQuickTextInput);
- if (!isComponentComplete())
- return;
-
- if (d->cursorComponent->isError()) {
- qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
- return;
- }
-
- if (!d->cursorComponent->isReady())
- return;
-
- if (d->cursorItem)
- delete d->cursorItem;
- QDeclarativeContext *creationContext = d->cursorComponent->creationContext();
- QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this));
- d->cursorItem = qobject_cast<QQuickItem*>(object);
- if (!d->cursorItem) {
- delete object;
- qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
- return;
- }
-
- QRectF r = cursorRectangle();
-
- QDeclarative_setParent_noEvent(d->cursorItem, this);
- d->cursorItem->setParentItem(this);
- d->cursorItem->setPos(r.topLeft());
- d->cursorItem->setHeight(r.height());
+ d->cursorPending = true;
+ QQuickTextUtil::createCursor(d);
}
/*!
QRectF QQuickTextInput::positionToRectangle(int pos) const
{
Q_D(const QQuickTextInput);
- if (pos > d->m_cursor)
+ if (d->m_echoMode == NoEcho)
+ pos = 0;
+ else if (pos > d->m_cursor)
pos += d->preeditAreaText().length();
- QTextLine l = d->m_textLayout.lineAt(0);
+ QTextLine l = d->m_textLayout.lineForTextPosition(pos);
return l.isValid()
- ? QRectF(l.cursorToX(pos) - d->hscroll, 0.0, d->m_cursorWidth, l.height())
+ ? QRectF(l.cursorToX(pos) - d->hscroll, l.y() - d->vscroll, 1, l.height())
: QRectF();
}
The cursor position type specifies how the cursor position should be resolved.
\list
- \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x.
- \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
+ \li TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x.
+ \li TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
\endlist
*/
-void QQuickTextInput::positionAt(QDeclarativeV8Function *args) const
+void QQuickTextInput::positionAt(QQmlV8Function *args) const
{
Q_D(const QQuickTextInput);
args->returnValue(v8::Int32::New(pos));
}
-int QQuickTextInputPrivate::positionAt(int x, int y, QTextLine::CursorPosition position) const
+int QQuickTextInputPrivate::positionAt(qreal x, qreal y, QTextLine::CursorPosition position) const
{
x += hscroll;
y += vscroll;
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) {
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 {
if (!ev->isAccepted())
QQuickImplicitSizeItem::inputMethodEvent(ev);
- if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0))
+ if (wasComposing != d->hasImState)
emit inputMethodComposingChanged();
}
d->selectWordAtPos(cursor);
event->setAccepted(true);
if (!d->hasPendingTripleClick()) {
- d->tripleClickStartPoint = event->localPos().toPoint();
+ d->tripleClickStartPoint = event->localPos();
d->tripleClickTimer.start();
}
} else {
d->pressPos = event->localPos();
- if (d->focusOnPress) {
- bool hadActiveFocus = hasActiveFocus();
- forceActiveFocus();
- // re-open input panel on press if already focused
- if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
- openSoftwareInputPanel();
- }
+ if (d->sendMouseEventToInputContext(event))
+ return;
+
if (d->selectByMouse) {
setKeepMouseGrab(false);
d->selectPressed = true;
- QPoint distanceVector = d->pressPos.toPoint() - d->tripleClickStartPoint;
+ QPointF distanceVector = d->pressPos - d->tripleClickStartPoint;
if (d->hasPendingTripleClick()
&& distanceVector.manhattanLength() < qApp->styleHints()->startDragDistance()) {
event->setAccepted(true);
}
}
- if (d->sendMouseEventToInputContext(event))
- return;
-
bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
int cursor = d->positionAt(event->localPos());
d->moveCursor(cursor, mark);
+
+ if (d->focusOnPress) {
+ bool hadActiveFocus = hasActiveFocus();
+ forceActiveFocus();
+ // re-open input panel on press if already focused
+ if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
+ qGuiApp->inputMethod()->show();
+ }
+
event->setAccepted(true);
}
int mousePos = tmp_cursor - m_cursor;
if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().length()) {
if (event->type() == QEvent::MouseButtonRelease) {
- qApp->inputPanel()->invokeAction(QInputPanel::Click, mousePos);
+ qApp->inputMethod()->invokeAction(QInputMethod::Click, mousePos);
}
return true;
}
const QRectF &oldGeometry)
{
Q_D(QQuickTextInput);
- if (newGeometry.width() != oldGeometry.width())
- d->updateLayout();
- updateCursorRectangle();
+ if (!d->inLayout) {
+ if (newGeometry.width() != oldGeometry.width())
+ d->updateLayout();
+ updateCursorRectangle();
+ }
QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
}
Q_Q(QQuickTextInput);
QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor);
const int preeditLength = m_textLayout.preeditAreaText().length();
- const int width = q->width();
- int widthUsed = currentLine.isValid() ? qRound(currentLine.naturalTextWidth()) : 0;
+ const qreal width = qMax<qreal>(0, q->width());
+ qreal cix = 0;
+ qreal widthUsed = 0;
+ if (currentLine.isValid()) {
+ cix = currentLine.cursorToX(m_cursor + preeditLength);
+ const qreal cursorWidth = cix >= 0 ? cix : width - cix;
+ widthUsed = qMax(currentLine.naturalTextWidth(), cursorWidth);
+ }
int previousScroll = hscroll;
if (!autoScroll || widthUsed <= width || m_echoMode == QQuickTextInput::NoEcho) {
hscroll = 0;
} else {
- int cix = qRound(currentLine.cursorToX(m_cursor + preeditLength));
+ Q_ASSERT(currentLine.isValid());
if (cix - hscroll >= width) {
// text doesn't fit, cursor is to the right of br (scroll right)
hscroll = cix - width;
// text doesn't fit, text document is to the left of br; align
// right
hscroll = widthUsed - width;
+ } else if (width - hscroll > widthUsed) {
+ // text doesn't fit, text document is to the right of br; align
+ // left
+ hscroll = width - widthUsed;
}
if (preeditLength > 0) {
// check to ensure long pre-edit text doesn't push the cursor
// off to the left
- cix = qRound(currentLine.cursorToX(m_cursor + qMax(0, m_preeditCursor - 1)));
+ cix = currentLine.cursorToX(m_cursor + qMax(0, m_preeditCursor - 1));
if (cix < hscroll)
hscroll = cix;
}
{
Q_Q(QQuickTextInput);
const int preeditLength = m_textLayout.preeditAreaText().length();
- const int height = q->height();
- int heightUsed = boundingRect.height();
- int previousScroll = vscroll;
+ const qreal height = qMax<qreal>(0, q->height());
+ qreal heightUsed = contentSize.height();
+ qreal previousScroll = vscroll;
if (!autoScroll || heightUsed <= height) {
// text fits in br; use vscroll for alignment
- switch (vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask)) {
- case Qt::AlignBottom:
- vscroll = heightUsed - height;
- break;
- case Qt::AlignVCenter:
- vscroll = (heightUsed - height) / 2;
- break;
- default:
- // Top
- vscroll = 0;
- break;
- }
+ vscroll = -QQuickTextUtil::alignedY(
+ heightUsed, height, vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask));
} else {
- QRectF r = m_textLayout.lineForTextPosition(m_cursor + preeditLength).rect();
- int top = qFloor(r.top());
- int bottom = qCeil(r.bottom());
+ QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + preeditLength);
+ QRectF r = currentLine.isValid() ? currentLine.rect() : QRectF();
+ qreal top = r.top();
+ int bottom = r.bottom();
if (bottom - vscroll >= height) {
// text doesn't fit, cursor is to the below the br (scroll down)
if (preeditLength > 0) {
// check to ensure long pre-edit text doesn't push the cursor
// off the top
- top = qRound(m_textLayout.lineForTextPosition(
- m_cursor + qMax(0, m_preeditCursor - 1)).rect().top());
- if (top < vscroll)
- vscroll = top;
+ currentLine = m_textLayout.lineForTextPosition(m_cursor + qMax(0, m_preeditCursor - 1));
+ top = currentLine.isValid() ? currentLine.rect().top() : 0;
+ if (top < vscroll)
+ vscroll = top;
}
}
if (previousScroll != vscroll)
textLayoutDirty = true;
}
+void QQuickTextInput::triggerPreprocess()
+{
+ Q_D(QQuickTextInput);
+ if (d->updateType == QQuickTextInputPrivate::UpdateNone)
+ d->updateType = QQuickTextInputPrivate::UpdateOnlyPreprocess;
+ update();
+}
+
QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
Q_UNUSED(data);
Q_D(QQuickTextInput);
+ if (d->updateType != QQuickTextInputPrivate::UpdatePaintNode && oldNode != 0) {
+ // Update done in preprocess() in the nodes
+ d->updateType = QQuickTextInputPrivate::UpdateNone;
+ return oldNode;
+ }
+
+ d->updateType = QQuickTextInputPrivate::UpdateNone;
+
QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
if (node == 0)
- node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
+ node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
d->textNode = node;
- if (!d->textLayoutDirty) {
+ if (!d->textLayoutDirty && oldNode != 0) {
QSGSimpleRectNode *cursorNode = node->cursorNode();
if (cursorNode != 0 && !isReadOnly()) {
cursorNode->setRect(cursorRectangle());
- if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
+ if (!d->cursorVisible || d->cursorItem || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
d->hideCursor();
} else {
d->showCursor();
}
}
} else {
+ node->setUseNativeRenderer(d->renderType == QQuickTextInput::NativeRendering);
node->deleteContent();
node->setMatrix(QMatrix4x4());
- QPoint offset = QPoint(0,0);
- QFontMetrics fm = QFontMetrics(d->font);
- if (d->autoScroll) {
+ QPointF offset(0, 0);
+ if (d->autoScroll && d->m_textLayout.lineCount() > 0) {
+ QFontMetricsF fm(d->font);
// the y offset is there to keep the baseline constant in case we have script changes in the text.
- offset = -QPoint(d->hscroll, d->vscroll + d->m_ascent - fm.ascent());
+ offset = -QPoint(d->hscroll, d->vscroll + d->m_textLayout.lineAt(0).ascent() - fm.ascent());
} else {
offset = -QPoint(d->hscroll, d->vscroll);
}
- if (!d->m_textLayout.text().isEmpty()) {
+ if (!d->m_textLayout.text().isEmpty() || !d->m_textLayout.preeditAreaText().isEmpty()) {
node->addTextLayout(offset, &d->m_textLayout, d->color,
- QQuickText::Normal, QColor(),
+ QQuickText::Normal, QColor(), QColor(),
d->selectionColor, d->selectedTextColor,
d->selectionStart(),
d->selectionEnd() - 1); // selectionEnd() returns first char after
case Qt::ImEnabled:
return QVariant((bool)(flags() & ItemAcceptsInputMethod));
case Qt::ImHints:
- return QVariant((int)inputMethodHints());
+ return QVariant((int) d->effectiveInputMethodHints());
case Qt::ImCursorRectangle:
return cursorRectangle();
case Qt::ImFont:
#endif // QT_NO_CLIPBOARD
/*!
+ Undoes the last operation if undo is \l {canUndo}{available}. Deselects any
+ current selection, and updates the selection start to the current cursor
+ position.
+*/
+
+void QQuickTextInput::undo()
+{
+ Q_D(QQuickTextInput);
+ if (!d->m_readOnly) {
+ d->internalUndo();
+ d->finishChange(-1, true);
+ }
+}
+
+/*!
+ Redoes the last operation if redo is \l {canRedo}{available}.
+*/
+
+void QQuickTextInput::redo()
+{
+ Q_D(QQuickTextInput);
+ if (!d->m_readOnly) {
+ d->internalRedo();
+ d->finishChange();
+ }
+}
+
+/*!
+ \qmlmethod void QtQuick2::TextInput::insert(int position, string text)
+
+ Inserts \a text into the TextInput at position.
+*/
+
+void QQuickTextInput::insert(int position, const QString &text)
+{
+ Q_D(QQuickTextInput);
+ if (d->m_echoMode == QQuickTextInput::Password) {
+ int delay = qGuiApp->styleHints()->passwordMaskDelay();
+ if (delay > 0)
+ d->m_passwordEchoTimer.start(delay, this);
+ }
+ if (position < 0 || position > d->m_text.length())
+ return;
+
+ const int priorState = d->m_undoState;
+
+ QString insertText = text;
+
+ if (d->hasSelectedText()) {
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
+ }
+ if (d->m_maskData) {
+ insertText = d->maskString(position, insertText);
+ for (int i = 0; i < insertText.length(); ++i) {
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::DeleteSelection, position + i, d->m_text.at(position + i), -1, -1));
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
+ }
+ d->m_text.replace(position, insertText.length(), insertText);
+ if (!insertText.isEmpty())
+ d->m_textDirty = true;
+ if (position < d->m_selend && position + insertText.length() > d->m_selstart)
+ d->m_selDirty = true;
+ } else {
+ int remaining = d->m_maxLength - d->m_text.length();
+ if (remaining != 0) {
+ insertText = insertText.left(remaining);
+ d->m_text.insert(position, insertText);
+ for (int i = 0; i < insertText.length(); ++i)
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::Insert, position + i, insertText.at(i), -1, -1));
+ if (d->m_cursor >= position)
+ d->m_cursor += insertText.length();
+ if (d->m_selstart >= position)
+ d->m_selstart += insertText.length();
+ if (d->m_selend >= position)
+ d->m_selend += insertText.length();
+ d->m_textDirty = true;
+ if (position >= d->m_selstart && position <= d->m_selend)
+ d->m_selDirty = true;
+ }
+ }
+
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
+ d->finishChange(priorState);
+
+ if (d->lastSelectionStart != d->lastSelectionEnd) {
+ if (d->m_selstart != d->lastSelectionStart) {
+ d->lastSelectionStart = d->m_selstart;
+ emit selectionStartChanged();
+ }
+ if (d->m_selend != d->lastSelectionEnd) {
+ d->lastSelectionEnd = d->m_selend;
+ emit selectionEndChanged();
+ }
+ }
+}
+
+/*!
+ \qmlmethod string QtQuick2::TextInput::getText(int start, int end)
+
+ Removes the section of text that is between the \a start and \a end positions from the TextInput.
+*/
+
+void QQuickTextInput::remove(int start, int end)
+{
+ Q_D(QQuickTextInput);
+
+ start = qBound(0, start, d->m_text.length());
+ end = qBound(0, end, d->m_text.length());
+
+ if (start > end)
+ qSwap(start, end);
+ else if (start == end)
+ return;
+
+ if (start < d->m_selend && end > d->m_selstart)
+ d->m_selDirty = true;
+
+ const int priorState = d->m_undoState;
+
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
+
+ if (start <= d->m_cursor && d->m_cursor < end) {
+ // cursor is within the selection. Split up the commands
+ // to be able to restore the correct cursor position
+ for (int i = d->m_cursor; i >= start; --i) {
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::DeleteSelection, i, d->m_text.at(i), -1, 1));
+ }
+ for (int i = end - 1; i > d->m_cursor; --i) {
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::DeleteSelection, i - d->m_cursor + start - 1, d->m_text.at(i), -1, -1));
+ }
+ } else {
+ for (int i = end - 1; i >= start; --i) {
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::RemoveSelection, i, d->m_text.at(i), -1, -1));
+ }
+ }
+ if (d->m_maskData) {
+ d->m_text.replace(start, end - start, d->clearString(start, end - start));
+ for (int i = 0; i < end - start; ++i) {
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::Insert, start + i, d->m_text.at(start + i), -1, -1));
+ }
+ } else {
+ d->m_text.remove(start, end - start);
+
+ if (d->m_cursor > start)
+ d->m_cursor -= qMin(d->m_cursor, end) - start;
+ if (d->m_selstart > start)
+ d->m_selstart -= qMin(d->m_selstart, end) - start;
+ if (d->m_selend > end)
+ d->m_selend -= qMin(d->m_selend, end) - start;
+ }
+ d->addCommand(QQuickTextInputPrivate::Command(
+ QQuickTextInputPrivate::SetSelection, d->m_cursor, 0, d->m_selstart, d->m_selend));
+
+ d->m_textDirty = true;
+ d->finishChange(priorState);
+
+ if (d->lastSelectionStart != d->lastSelectionEnd) {
+ if (d->m_selstart != d->lastSelectionStart) {
+ d->lastSelectionStart = d->m_selstart;
+ emit selectionStartChanged();
+ }
+ if (d->m_selend != d->lastSelectionEnd) {
+ d->lastSelectionEnd = d->m_selend;
+ emit selectionEndChanged();
+ }
+ }
+}
+
+
+/*!
\qmlmethod void QtQuick2::TextInput::selectWord()
Causes the word closest to the current cursor position to be selected.
}
/*!
- \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode
+ \qmlproperty enumeration QtQuick2::TextInput::mouseSelectionMode
Specifies how text should be selected using a mouse.
\list
- \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
- \o TextInput.SelectWords - The selection is updated with whole words.
+ \li TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
+ \li TextInput.SelectWords - The selection is updated with whole words.
\endlist
This property only applies when \l selectByMouse is true.
}
/*!
+ \qmlproperty bool QtQuick2::TextInput::persistentSelection
+
+ Whether the TextInput should keep its selection when it loses active focus to another
+ item in the scene. By default this is set to false;
+*/
+
+bool QQuickTextInput::persistentSelection() const
+{
+ Q_D(const QQuickTextInput);
+ return d->persistentSelection;
+}
+
+void QQuickTextInput::setPersistentSelection(bool on)
+{
+ Q_D(QQuickTextInput);
+ if (d->persistentSelection == on)
+ return;
+ d->persistentSelection = on;
+ emit persistentSelectionChanged();
+}
+
+#ifndef QT_NO_CLIPBOARD
+/*!
\qmlproperty bool QtQuick2::TextInput::canPaste
Returns true if the TextInput is writable and the content of the clipboard is
- suitable for pasting into the TextEdit.
+ suitable for pasting into the TextInput.
*/
bool QQuickTextInput::canPaste() const
{
}
return d->canPaste;
}
+#endif
+
+/*!
+ \qmlproperty bool QtQuick2::TextInput::canUndo
+
+ Returns true if the TextInput is writable and there are previous operations
+ that can be undone.
+*/
+
+bool QQuickTextInput::canUndo() const
+{
+ Q_D(const QQuickTextInput);
+ return d->canUndo;
+}
+
+/*!
+ \qmlproperty bool QtQuick2::TextInput::canRedo
+
+ Returns true if the TextInput is writable and there are \l {undo}{undone}
+ operations that can be redone.
+*/
+
+bool QQuickTextInput::canRedo() const
+{
+ Q_D(const QQuickTextInput);
+ return d->canRedo;
+}
+
+/*!
+ \qmlproperty real QtQuick2::TextInput::contentWidth
+
+ Returns the width of the text, including the width past the width
+ which is covered due to insufficient wrapping if \l wrapMode is set.
+*/
+
+qreal QQuickTextInput::contentWidth() const
+{
+ Q_D(const QQuickTextInput);
+ return d->contentSize.width();
+}
+
+/*!
+ \qmlproperty real QtQuick2::TextInput::contentHeight
+
+ Returns the height of the text, including the height past the height
+ that is covered if the text does not fit within the set height.
+*/
+
+qreal QQuickTextInput::contentHeight() const
+{
+ Q_D(const QQuickTextInput);
+ return d->contentSize.height();
+}
void QQuickTextInput::moveCursorSelection(int position)
{
basis. If not specified the selection mode will default to TextInput.SelectCharacters.
\list
- \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
+ \li TextInput.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
the previous cursor position) to the specified position.
- \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
+ \li TextInput.SelectWords - Sets the selectionStart and selectionEnd to include all
words between the specified position and the previous cursor position. Words partially in the
range are included.
\endlist
}
}
-/*!
- \qmlmethod void QtQuick2::TextInput::openSoftwareInputPanel()
-
- Opens software input panels like virtual keyboards for typing, useful for
- customizing when you want the input keyboard to be shown and hidden in
- your application.
-
- By default the opening of input panels follows the platform style. Input panels are
- always closed if no editor has active focus.
-
- You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
- and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
- the behavior you want.
-
- Only relevant on platforms, which provide virtual keyboards.
-
- \qml
- import QtQuick 1.0
- TextInput {
- id: textInput
- text: "Hello world!"
- activeFocusOnPress: false
- MouseArea {
- anchors.fill: parent
- onClicked: {
- if (!textInput.activeFocus) {
- textInput.forceActiveFocus()
- textInput.openSoftwareInputPanel();
- } else {
- textInput.focus = false;
- }
- }
- onPressAndHold: textInput.closeSoftwareInputPanel();
- }
- }
- \endqml
-*/
-void QQuickTextInput::openSoftwareInputPanel()
-{
- if (qGuiApp)
- qGuiApp->inputPanel()->show();
-}
-
-/*!
- \qmlmethod void QtQuick2::TextInput::closeSoftwareInputPanel()
-
- Closes a software input panel like a virtual keyboard shown on the screen, useful
- for customizing when you want the input keyboard to be shown and hidden in
- your application.
-
- By default the opening of input panels follows the platform style. Input panels are
- always closed if no editor has active focus.
-
- You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
- and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
- the behavior you want.
-
- Only relevant on platforms, which provide virtual keyboards.
-
- \qml
- import QtQuick 1.0
- TextInput {
- id: textInput
- text: "Hello world!"
- activeFocusOnPress: false
- MouseArea {
- anchors.fill: parent
- onClicked: {
- if (!textInput.activeFocus) {
- textInput.forceActiveFocus();
- textInput.openSoftwareInputPanel();
- } else {
- textInput.focus = false;
- }
- }
- onPressAndHold: textInput.closeSoftwareInputPanel();
- }
- }
- \endqml
-*/
-void QQuickTextInput::closeSoftwareInputPanel()
-{
- if (qGuiApp)
- qGuiApp->inputPanel()->hide();
-}
-
void QQuickTextInput::focusInEvent(QFocusEvent *event)
{
Q_D(const QQuickTextInput);
if (d->focusOnPress && !d->m_readOnly)
- openSoftwareInputPanel();
+ qGuiApp->inputMethod()->show();
QQuickImplicitSizeItem::focusInEvent(event);
}
Q_D(QQuickTextInput);
if (change == ItemActiveFocusHasChanged) {
bool hasFocus = value.boolValue;
- d->focused = hasFocus;
- setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus()
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
+ setCursorVisible(hasFocus);
if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) {
-#else
- if (!hasFocus && d->m_passwordEchoEditing) {
-#endif
d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
}
if (!hasFocus) {
- d->commitPreedit();
- d->deselect();
+ if (!d->persistentSelection)
+ d->deselect();
+ disconnect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
+ this, SLOT(q_updateAlignment()));
+ } else {
+ q_updateAlignment();
+ connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
+ this, SLOT(q_updateAlignment()));
}
}
QQuickItem::itemChange(change, value);
bool QQuickTextInput::isInputMethodComposing() const
{
Q_D(const QQuickTextInput);
- return d->preeditAreaText().length() > 0;
+ return d->hasImState;
}
void QQuickTextInputPrivate::init()
q->setFlag(QQuickItem::ItemAcceptsInputMethod);
q->setFlag(QQuickItem::ItemHasContents);
#ifndef QT_NO_CLIPBOARD
- q->connect(q, SIGNAL(readOnlyChanged(bool)),
- q, SLOT(q_canPasteChanged()));
q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()),
q, SLOT(q_canPasteChanged()));
#endif // QT_NO_CLIPBOARD
- imHints &= ~Qt::ImhMultiLine;
- oldValidity = hasAcceptableInput(m_text);
lastSelectionStart = 0;
lastSelectionEnd = 0;
- selectedTextColor = m_palette.color(QPalette::HighlightedText);
- selectionColor = m_palette.color(QPalette::Highlight);
determineHorizontalAlignment();
if (!qmlDisableDistanceField()) {
QTextOption option = m_textLayout.textOption();
- option.setUseDesignMetrics(true);
+ option.setUseDesignMetrics(renderType != QQuickTextInput::NativeRendering);
m_textLayout.setTextOption(option);
}
}
d->updateHorizontalScroll();
d->updateVerticalScroll();
+ d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
update();
- updateMicroFocus();
emit cursorRectangleChanged();
if (d->cursorItem) {
QRectF r = cursorRectangle();
d->cursorItem->setPos(r.topLeft());
d->cursorItem->setHeight(r.height());
}
+ updateInputMethod(Qt::ImCursorRectangle);
}
void QQuickTextInput::selectionChanged()
{
Q_D(QQuickTextInput);
d->textLayoutDirty = true; //TODO: Only update rect in selection
+ d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
update();
emit selectedTextChanged();
{
Q_D(const QQuickTextInput);
- QRectF r = d->boundingRect;
- int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->m_cursorWidth;
+ int cursorWidth = d->cursorItem ? 0 : 1;
+
+ qreal hscroll = d->hscroll;
+ if (!d->autoScroll || d->contentSize.width() < width())
+ hscroll -= QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign());
// Could include font max left/right bearings to either side of rectangle.
+ QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height());
+ r.setRight(r.right() + cursorWidth);
+ return r;
+}
+
+QRectF QQuickTextInput::clipRect() const
+{
+ Q_D(const QQuickTextInput);
+ int cursorWidth = d->cursorItem ? d->cursorItem->width() : 1;
+
+ // Could include font max left/right bearings to either side of rectangle.
+ QRectF r = QQuickImplicitSizeItem::clipRect();
r.setRight(r.right() + cursorWidth);
- r.translate(-d->hscroll, -d->vscroll);
return r;
}
}
+void QQuickTextInput::q_updateAlignment()
+{
+ Q_D(QQuickTextInput);
+ if (d->determineHorizontalAlignment()) {
+ d->updateLayout();
+ updateCursorRectangle();
+ }
+}
+
// ### these should come from QStyleHints
const int textCursorWidth = 1;
const bool fullWidthSelection = true;
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);
str[cursor - 1] = uc;
}
}
-#endif
} else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
str.fill(m_passwordCharacter);
}
}
}
+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);
if (!q->isComponentComplete())
return;
+
QTextOption option = m_textLayout.textOption();
- option.setTextDirection(m_layoutDirection);
- option.setFlags(QTextOption::IncludeTrailingSpaces);
+ option.setTextDirection(layoutDirection());
option.setWrapMode(QTextOption::WrapMode(wrapMode));
option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
m_textLayout.setTextOption(option);
m_textLayout.setFont(font);
- boundingRect = QRectF();
m_textLayout.beginLayout();
+
QTextLine line = m_textLayout.createLine();
+ if (requireImplicitWidth) {
+ line.setLineWidth(INT_MAX);
+ const bool wasInLayout = inLayout;
+ inLayout = true;
+ q->setImplicitWidth(qCeil(line.naturalTextWidth()));
+ inLayout = wasInLayout;
+ if (inLayout) // probably the result of a binding loop, but by letting it
+ return; // get this far we'll get a warning to that effect.
+ }
qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
qreal height = 0;
- QTextLine firstLine = line;
+ qreal width = 0;
do {
line.setLineWidth(lineWidth);
- line.setPosition(QPointF(line.position().x(), height));
- boundingRect = boundingRect.united(line.naturalTextRect());
+ line.setPosition(QPointF(0, height));
height += line.height();
+ width = qMax(width, line.naturalTextWidth());
+
line = m_textLayout.createLine();
} while (line.isValid());
m_textLayout.endLayout();
option.setWrapMode(QTextOption::NoWrap);
m_textLayout.setTextOption(option);
- m_ascent = qRound(firstLine.ascent());
textLayoutDirty = true;
+ const QSizeF previousSize = contentSize;
+ contentSize = QSizeF(width, height);
+
+ updateType = UpdatePaintNode;
q->update();
- q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
+ if (!requireImplicitWidth && !q->widthValid())
+ q->setImplicitSize(width, height);
+ else
+ q->setImplicitHeight(height);
+
+ if (previousSize != contentSize)
+ emit q->contentSizeChanged();
}
#ifndef QT_NO_CLIPBOARD
/*!
\internal
-
- Exits preedit mode and commits parts marked as tentative commit
*/
void QQuickTextInputPrivate::commitPreedit()
{
- if (!composeMode())
+ Q_Q(QQuickTextInput);
+
+ if (!hasImState)
return;
- qApp->inputPanel()->reset();
+ qApp->inputMethod()->commit();
- if (!m_tentativeCommit.isEmpty()) {
- internalInsert(m_tentativeCommit);
- m_tentativeCommit.clear();
- finishChange(-1, true/*not used, not documented*/, false);
- }
+ if (!hasImState)
+ return;
+
+ QInputMethodEvent ev;
+ QCoreApplication::sendEvent(q, &ev);
+}
+
+void QQuickTextInputPrivate::cancelPreedit()
+{
+ Q_Q(QQuickTextInput);
+
+ if (!hasImState)
+ return;
+
+ qApp->inputMethod()->reset();
- m_preeditCursor = 0;
- m_textLayout.setPreeditArea(-1, QString());
- m_textLayout.clearAdditionalFormats();
- updateLayout();
+ QInputMethodEvent ev;
+ QCoreApplication::sendEvent(q, &ev);
}
/*!
}
emit q->selectionChanged();
emitCursorPositionChanged();
-}
-
-/*!
- \internal
-
- Initializes the line control with a starting text value of \a txt.
-*/
-void QQuickTextInputPrivate::init(const QString &txt)
-{
- m_text = txt;
-
- updateDisplayText();
- m_cursor = m_text.length();
+ q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
+ | Qt::ImCursorPosition | Qt::ImCurrentSelection);
}
/*!
emit q->selectionChanged();
}
emitCursorPositionChanged();
+ q->updateInputMethod();
}
/*!
#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;
m_textLayout.setAdditionalFormats(formats);
updateDisplayText(/*force*/ true);
- if (cursorPositionChanged)
- emitCursorPositionChanged();
- else if (m_preeditCursor != oldPreeditCursor)
+ if ((cursorPositionChanged && !emitCursorPositionChanged())
+ || m_preeditCursor != oldPreeditCursor
+ || isGettingInput) {
q->updateCursorRectangle();
-
- bool tentativeCommitChanged = m_tentativeCommit != event->tentativeCommitString();
-
- if (tentativeCommitChanged) {
- m_textDirty = true;
- m_tentativeCommit = event->tentativeCommitString();
}
- if (isGettingInput || tentativeCommitChanged)
+ if (isGettingInput)
finishChange(priorState);
- if (selectionChange)
+ q->setCursorVisible(cursorVisible);
+
+ if (selectionChange) {
emit q->selectionChanged();
+ q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
+ | Qt::ImCursorPosition | Qt::ImCurrentSelection);
+ }
}
/*!
Q_Q(QQuickTextInput);
Q_UNUSED(update)
+ bool inputMethodAttributesChanged = m_textDirty || m_selDirty;
+ bool alignmentChanged = false;
if (m_textDirty) {
// do validation
bool wasValidInput = m_validInput;
+ bool wasAcceptable = m_acceptableInput;
m_validInput = true;
+ m_acceptableInput = true;
#ifndef QT_NO_VALIDATOR
if (m_validator) {
QString textCopy = m_text;
int cursorCopy = m_cursor;
- m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
+ QValidator::State state = m_validator->validate(textCopy, cursorCopy);
+ m_validInput = state != QValidator::Invalid;
+ m_acceptableInput = state == QValidator::Acceptable;
if (m_validInput) {
if (m_text != textCopy) {
internalSetText(textCopy, cursorCopy);
return true;
}
m_cursor = cursorCopy;
-
- if (!m_tentativeCommit.isEmpty()) {
- textCopy.insert(m_cursor, m_tentativeCommit);
- bool validInput = m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid;
- if (!validInput)
- m_tentativeCommit.clear();
- }
- } else {
- m_tentativeCommit.clear();
}
}
#endif
return false;
internalUndo(validateFromState);
m_history.resize(m_undoState);
- if (m_modifiedState > m_undoState)
- m_modifiedState = -1;
m_validInput = true;
+ m_acceptableInput = wasAcceptable;
m_textDirty = false;
}
- updateDisplayText();
if (m_textDirty) {
m_textDirty = false;
m_preeditDirty = false;
- determineHorizontalAlignment();
+ alignmentChanged = determineHorizontalAlignment();
emit q->textChanged();
}
- if (m_validInput != wasValidInput)
+ updateDisplayText(alignmentChanged);
+
+ if (m_acceptableInput != wasAcceptable)
emit q->acceptableInputChanged();
}
if (m_preeditDirty) {
m_preeditDirty = false;
- determineHorizontalAlignment();
+ if (determineHorizontalAlignment()) {
+ alignmentChanged = true;
+ updateLayout();
+ }
}
+
if (m_selDirty) {
m_selDirty = false;
emit q->selectionChanged();
}
- emitCursorPositionChanged();
+
+ inputMethodAttributesChanged |= (m_cursor != m_lastCursorPos);
+ if (inputMethodAttributesChanged)
+ q->updateInputMethod();
+ emitUndoRedoChanged();
+
+ if (!emitCursorPositionChanged() && alignmentChanged)
+ q->updateCursorRectangle();
+
return true;
}
m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
}
m_history.clear();
- m_modifiedState = m_undoState = 0;
+ m_undoState = 0;
m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
m_textDirty = (oldText != m_text);
#ifdef QT_NO_ACCESSIBILITY
Q_UNUSED(changed)
#else
- if (changed)
- QAccessible::updateAccessibility(q, 0, QAccessible::TextUpdated);
+ if (changed) {
+ QAccessibleTextUpdateEvent ev(q, 0, oldText, m_text);
+ QAccessible::updateAccessibility(&ev);
+ }
#endif
}
*/
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) {
Otherwise returns false
*/
-bool QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
+QQuickTextInputPrivate::ValidatorState QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
{
#ifndef QT_NO_VALIDATOR
QString textCopy = str;
int cursorCopy = m_cursor;
- if (m_validator && m_validator->validate(textCopy, cursorCopy)
- != QValidator::Acceptable)
- return false;
+ if (m_validator) {
+ QValidator::State state = m_validator->validate(textCopy, cursorCopy);
+ if (state != QValidator::Acceptable)
+ return ValidatorState(state);
+ }
#endif
if (!m_maskData)
- return true;
+ return AcceptableInput;
if (str.length() != m_maxLength)
- return false;
+ return InvalidInput;
for (int i=0; i < m_maxLength; ++i) {
if (m_maskData[i].separator) {
if (str.at(i) != m_maskData[i].maskChar)
- return false;
+ return InvalidInput;
} else {
if (!isValidInput(str.at(i), m_maskData[i].maskChar))
- return false;
+ return InvalidInput;
}
}
- return true;
+ return AcceptableInput;
}
/*!
QString s;
int end = qMin(m_maxLength, (int)str.length());
- for (int i = 0; i < end; ++i)
+ for (int i = 0; i < end; ++i) {
if (m_maskData[i].separator)
s += m_maskData[i].maskChar;
- else
- if (str[i] != m_blank)
- s += str[i];
+ else if (str[i] != m_blank)
+ s += str[i];
+ }
return s;
}
}
}
m_textDirty = true;
- emitCursorPositionChanged();
}
void QQuickTextInputPrivate::internalRedo()
}
}
m_textDirty = true;
- emitCursorPositionChanged();
+}
+
+void QQuickTextInputPrivate::emitUndoRedoChanged()
+{
+ Q_Q(QQuickTextInput);
+ const bool previousUndo = canUndo;
+ const bool previousRedo = canRedo;
+
+ canUndo = isUndoAvailable();
+ canRedo = isRedoAvailable();
+
+ if (previousUndo != canUndo)
+ emit q->canUndoChanged();
+ if (previousRedo != canRedo)
+ emit q->canRedoChanged();
}
/*!
If the current cursor position differs from the last emitted cursor
position, emits cursorPositionChanged().
*/
-void QQuickTextInputPrivate::emitCursorPositionChanged()
+bool QQuickTextInputPrivate::emitCursorPositionChanged()
{
Q_Q(QQuickTextInput);
if (m_cursor != m_lastCursorPos) {
q->updateCursorRectangle();
emit q->cursorPositionChanged();
- // XXX todo - not in 4.8?
- #if 0
- resetCursorBlinkTimer();
- #endif
if (!hasSelectedText()) {
if (lastSelectionStart != m_cursor) {
}
#ifndef QT_NO_ACCESSIBILITY
- QAccessible::updateAccessibility(q, 0, QAccessible::TextCaretMoved);
+ QAccessibleTextCursorEvent ev(q, m_cursor);
+ QAccessible::updateAccessibility(&ev);
#endif
+
+ return true;
}
+ return false;
}
m_blinkStatus = 1;
} else {
m_blinkTimer = 0;
- if (m_blinkStatus == 1)
+ if (m_blinkStatus == 1) {
+ updateType = UpdatePaintNode;
q->update();
+ }
}
m_blinkPeriod = msec;
}
-void QQuickTextInputPrivate::resetCursorBlinkTimer()
-{
- Q_Q(QQuickTextInput);
- if (m_blinkPeriod == 0 || m_blinkTimer == 0)
- return;
- q->killTimer(m_blinkTimer);
- m_blinkTimer = q->startTimer(m_blinkPeriod / 2);
- m_blinkStatus = 1;
-}
-
void QQuickTextInput::timerEvent(QTimerEvent *event)
{
Q_D(QQuickTextInput);
if (event->timerId() == d->m_blinkTimer) {
d->m_blinkStatus = !d->m_blinkStatus;
+ d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
update();
- } else if (event->timerId() == d->m_deleteAllTimer) {
- killTimer(d->m_deleteAllTimer);
- d->m_deleteAllTimer = 0;
- d->clear();
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
} else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
d->m_passwordEchoTimer.stop();
d->updateDisplayText();
-#endif
+ updateCursorRectangle();
}
}
}
#ifndef QT_NO_SHORTCUT
else if (event == QKeySequence::Undo) {
- if (!m_readOnly)
- undo();
+ q->undo();
}
else if (event == QKeySequence::Redo) {
- if (!m_readOnly)
- redo();
+ q->redo();
}
else if (event == QKeySequence::SelectAll) {
selectAll();