#include "qquicktextinput_p.h"
#include "qquicktextinput_p_p.h"
#include "qquickcanvas.h"
+#include "qquicktextutil_p.h"
#include <private/qqmlglobal_p.h>
+
#include <QtCore/qcoreapplication.h>
#include <QtQml/qqmlinfo.h>
#include <QtGui/qevent.h>
/*!
\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.
d->checkIsValid();
d->updateLayout();
updateCursorRectangle();
- if (d->cursorComponent && d->cursorComponent->isReady())
- createCursor();
+ if (d->cursorComponent && isCursorVisible())
+ QQuickTextUtil::createCursor(d);
}
/*!
return false;
}
+Qt::LayoutDirection QQuickTextInputPrivate::textDirection() const
+{
+ QString text = m_text;
+ if (text.isEmpty())
+ text = m_textLayout.preeditAreaText();
+
+ const QChar *character = text.constData();
+ while (!character->isNull()) {
+ switch (character->direction()) {
+ case QChar::DirL:
+ return Qt::LeftToRight;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirAN:
+ return Qt::RightToLeft;
+ default:
+ break;
+ }
+ character++;
+ }
+ return Qt::LayoutDirectionAuto;
+}
+
+Qt::LayoutDirection QQuickTextInputPrivate::layoutDirection() const
+{
+ Qt::LayoutDirection direction = m_layoutDirection;
+ if (direction == Qt::LayoutDirectionAuto) {
+ direction = textDirection();
+ if (direction == Qt::LayoutDirectionAuto)
+ direction = qApp->inputMethod()->inputDirection();
+ }
+ return (direction == Qt::LayoutDirectionAuto) ? Qt::LeftToRight : direction;
+}
+
bool QQuickTextInputPrivate::determineHorizontalAlignment()
{
if (hAlignImplicit) {
// if no explicit alignment has been set, follow the natural layout direction of the text
- QString text = q_func()->text();
- if (text.isEmpty())
- text = m_textLayout.preeditAreaText();
- bool isRightToLeft = text.isEmpty() ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft
- : text.isRightToLeft();
- return setHAlign(isRightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
+ Qt::LayoutDirection direction = textDirection();
+ if (direction == Qt::LayoutDirectionAuto)
+ direction = qApp->inputMethod()->inputDirection();
+ return setHAlign(direction == Qt::RightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
}
return false;
}
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);
}
/*!
\qmlclass IntValidator QIntValidator
\inqmlmodule QtQuick 2
- \ingroup qml-basic-visual-elements
+ \ingroup qtquick-text-utility
+ \brief Defines a validator for integer values
This element provides a validator for integer values.
/*!
\qmlclass DoubleValidator QDoubleValidator
\inqmlmodule QtQuick 2
- \ingroup qml-basic-visual-elements
+ \ingroup qtquick-text-utility
+ \brief Defines a validator for non-integer numbers
This element provides a validator for non-integer numbers.
/*!
\qmlclass RegExpValidator QRegExpValidator
\inqmlmodule QtQuick 2
- \ingroup qml-basic-visual-elements
+ \ingroup qtquick-text-utility
+ \brief Provides a string validator
This element provides a validator, which counts as valid any string which
matches a specified regular expression.
void QQuickTextInput::setCursorDelegate(QQmlComponent* c)
{
Q_D(QQuickTextInput);
- if (d->cursorComponent == c)
- return;
-
- d->cursorComponent = c;
- if (!c) {
- //note that the components are owned by something else
- delete d->cursorItem;
- d->cursorItem = 0;
- } else {
- d->startCreatingCursor();
- }
-
- emit cursorDelegateChanged();
-}
-
-void QQuickTextInputPrivate::startCreatingCursor()
-{
- Q_Q(QQuickTextInput);
- if (cursorComponent->isReady()) {
- q->createCursor();
- } else if (cursorComponent->isLoading()) {
- q->connect(cursorComponent, SIGNAL(statusChanged(int)),
- q, SLOT(createCursor()));
- } else { // isError
- qmlInfo(q, cursorComponent->errors()) << QQuickTextInput::tr("Could not load cursor delegate");
- }
+ QQuickTextUtil::setCursorDelegate(d, c);
}
void QQuickTextInput::createCursor()
{
Q_D(QQuickTextInput);
- if (!isComponentComplete())
- return;
-
- if (d->cursorComponent->isError()) {
- qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
- return;
- }
-
- if (!d->cursorComponent->isReady())
- return;
-
- if (d->cursorItem)
- delete d->cursorItem;
- QQmlContext *creationContext = d->cursorComponent->creationContext();
- QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this));
- d->cursorItem = qobject_cast<QQuickItem*>(object);
- if (!d->cursorItem) {
- delete object;
- qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
- return;
- }
-
- QRectF r = cursorRectangle();
-
- QQml_setParent_noEvent(d->cursorItem, this);
- d->cursorItem->setParentItem(this);
- d->cursorItem->setPos(r.topLeft());
- d->cursorItem->setHeight(r.height());
+ d->cursorPending = true;
+ QQuickTextUtil::createCursor(d);
}
/*!
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) {
Q_Q(QQuickTextInput);
const int preeditLength = m_textLayout.preeditAreaText().length();
const qreal height = qMax<qreal>(0, q->height());
- qreal heightUsed = boundingRect.height();
+ qreal heightUsed = contentSize.height();
qreal previousScroll = vscroll;
if (!autoScroll || heightUsed <= height) {
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();
emit persistentSelectionChanged();
}
+#ifndef QT_NO_CLIPBOARD
/*!
\qmlproperty bool QtQuick2::TextInput::canPaste
}
return d->canPaste;
}
+#endif
/*!
\qmlproperty bool QtQuick2::TextInput::canUndo
qreal QQuickTextInput::contentWidth() const
{
Q_D(const QQuickTextInput);
- return d->boundingRect.width();
+ return d->contentSize.width();
}
/*!
qreal QQuickTextInput::contentHeight() const
{
Q_D(const QQuickTextInput);
- return d->boundingRect.height();
+ return d->contentSize.height();
}
void QQuickTextInput::moveCursorSelection(int position)
{
Q_D(const QQuickTextInput);
+ int cursorWidth = d->cursorItem ? 0 : 1;
+
+ qreal hscroll = d->hscroll;
+ if (!d->autoScroll || d->contentSize.width() < width()) {
+ switch (effectiveHAlign()) {
+ case AlignLeft:
+ break;
+ case AlignRight:
+ hscroll += d->contentSize.width() - width();
+ break;
+ case AlignHCenter:
+ hscroll += (d->contentSize.width() - width()) / 2;
+ break;
+ }
+ }
+
+ // Could include font max left/right bearings to either side of rectangle.
+ QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height());
+ r.setRight(r.right() + cursorWidth);
+ return r;
+}
+
+QRectF QQuickTextInput::clipRect() const
+{
+ Q_D(const QQuickTextInput);
+
int cursorWidth = d->cursorItem ? d->cursorItem->width() : 1;
// Could include font max left/right bearings to either side of rectangle.
- QRectF r = QQuickImplicitSizeItem::boundingRect();
+ QRectF r = QQuickImplicitSizeItem::clipRect();
r.setRight(r.right() + cursorWidth);
return r;
}
if (!q->isComponentComplete())
return;
- const QRectF previousRect = boundingRect;
QTextOption option = m_textLayout.textOption();
option.setTextDirection(layoutDirection());
m_textLayout.setTextOption(option);
m_textLayout.setFont(font);
- boundingRect = QRectF();
m_textLayout.beginLayout();
+
QTextLine line = m_textLayout.createLine();
if (requireImplicitWidth) {
line.setLineWidth(INT_MAX);
}
qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
qreal height = 0;
+ qreal width = 0;
do {
line.setLineWidth(lineWidth);
- line.setPosition(QPointF(line.position().x(), height));
- boundingRect = boundingRect.united(line.naturalTextRect());
+ line.setPosition(QPointF(0, height));
height += line.height();
+ width = qMax(width, line.naturalTextWidth());
+
line = m_textLayout.createLine();
} while (line.isValid());
m_textLayout.endLayout();
textLayoutDirty = true;
+ const QSizeF previousSize = contentSize;
+ contentSize = QSizeF(width, height);
+
updateType = UpdatePaintNode;
q->update();
if (!requireImplicitWidth && !q->widthValid())
- q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
+ q->setImplicitSize(width, height);
else
- q->setImplicitHeight(qCeil(boundingRect.height()));
+ q->setImplicitHeight(height);
- if (previousRect != boundingRect)
+ if (previousSize != contentSize)
emit q->contentSizeChanged();
}
m_textLayout.setPreeditArea(m_cursor, event->preeditString());
#endif //QT_NO_IM
const int oldPreeditCursor = m_preeditCursor;
- const bool oldCursorVisible = cursorVisible;
m_preeditCursor = event->preeditString().length();
hasImState = !event->preeditString().isEmpty();
- cursorVisible = true;
+ bool cursorVisible = true;
QList<QTextLayout::FormatRange> formats;
for (int i = 0; i < event->attributes().size(); ++i) {
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
m_textLayout.setAdditionalFormats(formats);
updateDisplayText(/*force*/ true);
- if (cursorPositionChanged) {
- emitCursorPositionChanged();
- } else if (m_preeditCursor != oldPreeditCursor) {
+ if ((cursorPositionChanged && !emitCursorPositionChanged())
+ || m_preeditCursor != oldPreeditCursor
+ || isGettingInput) {
q->updateCursorRectangle();
}
if (isGettingInput)
finishChange(priorState);
- if (cursorVisible != oldCursorVisible)
- emit q->cursorVisibleChanged(cursorVisible);
+ q->setCursorVisible(cursorVisible);
if (selectionChange) {
emit q->selectionChanged();
emit q->selectionChanged();
}
- inputMethodAttributesChanged |= (m_cursor == m_lastCursorPos);
+ inputMethodAttributesChanged |= (m_cursor != m_lastCursorPos);
if (inputMethodAttributesChanged)
q->updateInputMethod();
emitUndoRedoChanged();
} else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
d->m_passwordEchoTimer.stop();
d->updateDisplayText();
+ updateCursorRectangle();
}
}