aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/declarative/whatsnew.qdoc3
-rw-r--r--src/quick/items/qquicktextinput.cpp389
-rw-r--r--src/quick/items/qquicktextinput_p.h36
-rw-r--r--src/quick/items/qquicktextinput_p_p.h40
-rw-r--r--tests/auto/qtquick2/qquicktextinput/data/horizontalAlignment.qml3
-rw-r--r--tests/auto/qtquick2/qquicktextinput/data/positionAt.qml1
-rw-r--r--tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp192
-rw-r--r--tests/testapplications/text/textinput.qml2
8 files changed, 489 insertions, 177 deletions
diff --git a/doc/src/declarative/whatsnew.qdoc b/doc/src/declarative/whatsnew.qdoc
index 2ae46714b6..ef23d15f62 100644
--- a/doc/src/declarative/whatsnew.qdoc
+++ b/doc/src/declarative/whatsnew.qdoc
@@ -127,6 +127,9 @@ Text improvements:
TextEdit:
- the default value of the textFormat property is now PlainText instead of AutoText.
+TextInput has new wrapMode and verticalAlignment properties, and the positionAt function now takes
+a y parameter.
+
PathView now has a \c currentItem property
ListView and GridView:
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 649e29d6bc..b6bc33c767 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -92,6 +92,18 @@ QQuickTextInput::~QQuickTextInput()
{
}
+void QQuickTextInput::componentComplete()
+{
+ Q_D(QQuickTextInput);
+
+ QQuickImplicitSizeItem::componentComplete();
+
+ d->updateLayout();
+ updateCursorRectangle();
+ if (d->cursorComponent && d->cursorComponent->isReady())
+ createCursor();
+}
+
/*!
\qmlproperty string QtQuick2::TextInput::text
@@ -251,12 +263,8 @@ void QQuickTextInput::setFont(const QFont &font)
d->font.setPointSizeF(size/2.0);
}
if (oldFont != d->font) {
- d->updateDisplayText();
- updateSize();
+ d->updateLayout();
updateCursorRectangle();
- if (d->cursorItem) {
- d->cursorItem->setHeight(QFontMetrics(d->font).height());
- }
}
emit fontChanged(d->sourceFont);
}
@@ -338,6 +346,7 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
/*!
\qmlproperty enumeration QtQuick2::TextInput::horizontalAlignment
\qmlproperty enumeration QtQuick2::TextInput::effectiveHorizontalAlignment
+ \qmlproperty enumeration QtQuick2::TextInput::verticalAlignment
Sets the horizontal alignment of the text within the TextInput item's
width and height. By default, the text alignment follows the natural alignment
@@ -353,6 +362,9 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
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.
+
When using the attached property LayoutMirroring::enabled to mirror application
layouts, the horizontal alignment of text will also be mirrored. However, the property
\c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
@@ -370,6 +382,7 @@ void QQuickTextInput::setHAlign(HAlignment align)
bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
d->hAlignImplicit = false;
if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
+ d->updateLayout();
updateCursorRectangle();
}
}
@@ -379,6 +392,7 @@ void QQuickTextInput::resetHAlign()
Q_D(QQuickTextInput);
d->hAlignImplicit = true;
if (d->determineHorizontalAlignment() && isComponentComplete()) {
+ d->updateLayout();
updateCursorRectangle();
}
}
@@ -429,6 +443,56 @@ bool QQuickTextInputPrivate::determineHorizontalAlignment()
return false;
}
+QQuickTextInput::VAlignment QQuickTextInput::vAlign() const
+{
+ Q_D(const QQuickTextInput);
+ return d->vAlign;
+}
+
+void QQuickTextInput::setVAlign(QQuickTextInput::VAlignment alignment)
+{
+ Q_D(QQuickTextInput);
+ if (alignment == d->vAlign)
+ return;
+ d->vAlign = alignment;
+ emit verticalAlignmentChanged(d->vAlign);
+ if (isComponentComplete()) {
+ updateCursorRectangle();
+ }
+}
+
+/*!
+ \qmlproperty enumeration QtQuick2::TextInput::wrapMode
+
+ Set this property to wrap the text to the TextEdit 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.
+ \endlist
+
+ The default is TextInput.NoWrap. If you set a width, consider using TextInput.Wrap.
+*/
+QQuickTextInput::WrapMode QQuickTextInput::wrapMode() const
+{
+ Q_D(const QQuickTextInput);
+ return d->wrapMode;
+}
+
+void QQuickTextInput::setWrapMode(WrapMode mode)
+{
+ Q_D(QQuickTextInput);
+ if (mode == d->wrapMode)
+ return;
+ d->wrapMode = mode;
+ d->updateLayout();
+ updateCursorRectangle();
+ emit wrapModeChanged();
+}
+
void QQuickTextInputPrivate::mirrorChange()
{
Q_Q(QQuickTextInput);
@@ -567,12 +631,20 @@ void QQuickTextInput::setCursorPosition(int cp)
QRect QQuickTextInput::cursorRectangle() const
{
Q_D(const QQuickTextInput);
- QTextLine l = d->m_textLayout.lineAt(0);
+
int c = d->m_cursor;
if (d->m_preeditCursor != -1)
c += d->m_preeditCursor;
- return QRect(qRound(l.cursorToX(c)) - d->hscroll, 0, d->m_cursorWidth, l.height());
+ if (d->m_echoMode == NoEcho || !isComponentComplete())
+ c = 0;
+ QTextLine l = d->m_textLayout.lineForTextPosition(c);
+ return QRect(
+ qRound(l.cursorToX(c) - d->hscroll),
+ qRound(l.y() - d->vscroll),
+ d->m_cursorWidth,
+ qCeil(l.height()));
}
+
/*!
\qmlproperty int QtQuick2::TextInput::selectionStart
@@ -686,7 +758,6 @@ void QQuickTextInput::setAutoScroll(bool b)
d->autoScroll = b;
//We need to repaint so that the scrolling is taking into account.
- updateSize(true);
updateCursorRectangle();
emit autoScrollChanged(d->autoScroll);
}
@@ -908,9 +979,8 @@ void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
d->m_echoMode = echo;
d->m_passwordEchoEditing = false;
d->updateInputMethodHints();
-
d->updateDisplayText();
- q_textChanged();
+ updateCursorRectangle();
emit echoModeChanged(echoMode());
}
@@ -982,6 +1052,9 @@ void QQuickTextInputPrivate::startCreatingCursor()
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;
@@ -1001,10 +1074,12 @@ void QQuickTextInput::createCursor()
return;
}
+ QRectF r = cursorRectangle();
+
QDeclarative_setParent_noEvent(d->cursorItem, this);
d->cursorItem->setParentItem(this);
- d->cursorItem->setX(d->cursorToX());
- d->cursorItem->setHeight(d->calculateTextHeight());
+ d->cursorItem->setPos(r.topLeft());
+ d->cursorItem->setHeight(r.height());
}
/*!
@@ -1022,19 +1097,22 @@ QRectF QQuickTextInput::positionToRectangle(int pos) const
if (pos > d->m_cursor)
pos += d->preeditAreaText().length();
QTextLine l = d->m_textLayout.lineAt(0);
- return QRectF( l.cursorToX(pos) - d->hscroll, 0.0, d->m_cursorWidth, l.height());
+ return QRectF(l.cursorToX(pos) - d->hscroll, 0.0, d->m_cursorWidth, l.height());
}
/*!
- \qmlmethod int QtQuick2::TextInput::positionAt(int x, CursorPosition position = CursorBetweenCharacters)
+ \qmlmethod int QtQuick2::TextInput::positionAt(real x, real y, CursorPosition position = CursorBetweenCharacters)
This function returns the character position at
- x pixels from the left of the textInput. Position 0 is before the
+ x and y pixels from the top left of the textInput. Position 0 is before the
first character, position 1 is after the first character but before the second,
and so on until position text.length, which is after all characters.
This means that for all x values before the first character this function returns 0,
- and for all x values after the last character this function returns text.length.
+ and for all x values after the last character this function returns text.length. If
+ the y value is above the text the position will be that of the nearest character on
+ the first line line and if it is below the text the position of the nearest character
+ on the last line will be returned.
The cursor position type specifies how the cursor position should be resolved.
@@ -1043,15 +1121,33 @@ QRectF QQuickTextInput::positionToRectangle(int pos) const
\o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
\endlist
*/
-int QQuickTextInput::positionAt(int x) const
-{
- return positionAt(x, CursorBetweenCharacters);
-}
-int QQuickTextInput::positionAt(int x, CursorPosition position) const
+void QQuickTextInput::positionAt(QDeclarativeV8Function *args) const
{
Q_D(const QQuickTextInput);
- int pos = d->m_textLayout.lineAt(0).xToCursor(x + d->hscroll, QTextLine::CursorPosition(position));
+
+ qreal x = 0;
+ qreal y = 0;
+ QTextLine::CursorPosition position = QTextLine::CursorBetweenCharacters;
+
+ if (args->Length() < 1)
+ return;
+
+ int i = 0;
+ v8::Local<v8::Value> arg = (*args)[i];
+ x = arg->NumberValue();
+
+ if (++i < args->Length()) {
+ arg = (*args)[i];
+ y = arg->NumberValue();
+ }
+
+ if (++i < args->Length()) {
+ arg = (*args)[i];
+ position = QTextLine::CursorPosition(arg->Int32Value());
+ }
+
+ int pos = d->positionAt(x, y, position);
const int cursor = d->m_cursor;
if (pos > cursor) {
const int preeditLength = d->preeditAreaText().length();
@@ -1059,7 +1155,22 @@ int QQuickTextInput::positionAt(int x, CursorPosition position) const
? pos - preeditLength
: cursor;
}
- return pos;
+ args->returnValue(v8::Int32::New(pos));
+}
+
+int QQuickTextInputPrivate::positionAt(int x, int y, QTextLine::CursorPosition position) const
+{
+ x += hscroll;
+ y += vscroll;
+ QTextLine line = m_textLayout.lineAt(0);
+ for (int i = 1; i < m_textLayout.lineCount(); ++i) {
+ QTextLine nextLine = m_textLayout.lineAt(i);
+
+ if (y < (line.rect().bottom() + nextLine.y()) / 2)
+ break;
+ line = nextLine;
+ }
+ return line.xToCursor(x, position);
}
void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
@@ -1107,7 +1218,7 @@ void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event)
if (d->selectByMouse && event->button() == Qt::LeftButton) {
d->commitPreedit();
- int cursor = d->xToPos(event->localPos().x());
+ int cursor = d->positionAt(event->localPos());
d->selectWordAtPos(cursor);
event->setAccepted(true);
if (!d->hasPendingTripleClick()) {
@@ -1150,7 +1261,7 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
return;
bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
- int cursor = d->xToPos(event->localPos().x());
+ int cursor = d->positionAt(event->localPos());
d->moveCursor(cursor, mark);
event->setAccepted(true);
}
@@ -1165,12 +1276,12 @@ void QQuickTextInput::mouseMoveEvent(QMouseEvent *event)
if (d->composeMode()) {
// start selection
- int startPos = d->xToPos(d->pressPos.x());
- int currentPos = d->xToPos(event->localPos().x());
+ int startPos = d->positionAt(d->pressPos);
+ int currentPos = d->positionAt(event->localPos());
if (startPos != currentPos)
d->setSelection(startPos, currentPos - startPos);
} else {
- moveCursorSelection(d->xToPos(event->localPos().x()), d->mouseSelectionMode);
+ moveCursorSelection(d->positionAt(event->localPos()), d->mouseSelectionMode);
}
event->setAccepted(true);
} else {
@@ -1205,7 +1316,7 @@ bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event)
{
#if !defined QT_NO_IM
if (composeMode()) {
- int tmp_cursor = xToPos(event->localPos().x());
+ int tmp_cursor = positionAt(event->localPos());
int mousePos = tmp_cursor - m_cursor;
if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().length()) {
if (event->type() == QEvent::MouseButtonRelease) {
@@ -1284,37 +1395,26 @@ bool QQuickTextInput::event(QEvent* ev)
void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry)
{
- if (newGeometry.width() != oldGeometry.width()) {
- updateSize();
- updateCursorRectangle();
- }
+ Q_D(QQuickTextInput);
+ if (newGeometry.width() != oldGeometry.width())
+ d->updateLayout();
+ updateCursorRectangle();
QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
}
void QQuickTextInputPrivate::updateHorizontalScroll()
{
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 = calculateTextWidth();
+ int widthUsed = currentLine.isValid() ? qRound(currentLine.naturalTextWidth()) : 0;
+ int previousScroll = hscroll;
- if (!autoScroll || widthUsed <= width) {
- QQuickTextInput::HAlignment effectiveHAlign = q->effectiveHAlign();
- // text fits in br; use hscroll for alignment
- switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) {
- case Qt::AlignRight:
- hscroll = widthUsed - width;
- break;
- case Qt::AlignHCenter:
- hscroll = (widthUsed - width) / 2;
- break;
- default:
- // Left
- hscroll = 0;
- break;
- }
+ if (!autoScroll || widthUsed <= width || m_echoMode == QQuickTextInput::NoEcho) {
+ hscroll = 0;
} else {
- int cix = qRound(cursorToX(m_cursor + preeditLength));
+ int cix = qRound(currentLine.cursorToX(m_cursor + preeditLength));
if (cix - hscroll >= width) {
// text doesn't fit, cursor is to the right of br (scroll right)
hscroll = cix - width;
@@ -1329,12 +1429,64 @@ void QQuickTextInputPrivate::updateHorizontalScroll()
if (preeditLength > 0) {
// check to ensure long pre-edit text doesn't push the cursor
// off to the left
- cix = qRound(cursorToX(
- m_cursor + qMax(0, m_preeditCursor - 1)));
+ cix = qRound(currentLine.cursorToX(m_cursor + qMax(0, m_preeditCursor - 1)));
if (cix < hscroll)
hscroll = cix;
}
}
+ if (previousScroll != hscroll)
+ textLayoutDirty = true;
+}
+
+void QQuickTextInputPrivate::updateVerticalScroll()
+{
+ Q_Q(QQuickTextInput);
+ const int preeditLength = m_textLayout.preeditAreaText().length();
+ const int height = q->height();
+ int heightUsed = boundingRect.height();
+ int 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;
+ }
+ } else {
+ QRectF r = m_textLayout.lineForTextPosition(m_cursor + preeditLength).rect();
+ int top = qFloor(r.top());
+ int bottom = qCeil(r.bottom());
+
+ if (bottom - vscroll >= height) {
+ // text doesn't fit, cursor is to the below the br (scroll down)
+ vscroll = bottom - height;
+ } else if (top - vscroll < 0 && vscroll < heightUsed) {
+ // text doesn't fit, cursor is above br (scroll up)
+ vscroll = top;
+ } else if (heightUsed - vscroll < height) {
+ // text doesn't fit, text document is to the left of br; align
+ // right
+ vscroll = heightUsed - height;
+ }
+ 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;
+ }
+ }
+ if (previousScroll != vscroll)
+ textLayoutDirty = true;
}
QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
@@ -1364,12 +1516,11 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
QPoint offset = QPoint(0,0);
QFontMetrics fm = QFontMetrics(d->font);
- QRect br(boundingRect().toRect());
if (d->autoScroll) {
// the y offset is there to keep the baseline constant in case we have script changes in the text.
- offset = br.topLeft() - QPoint(d->hscroll, d->ascent() - fm.ascent());
+ offset = -QPoint(d->hscroll, d->vscroll + d->m_ascent - fm.ascent());
} else {
- offset = QPoint(d->hscroll, 0);
+ offset = -QPoint(d->hscroll, d->vscroll);
}
if (!d->m_textLayout.text().isEmpty()) {
@@ -1554,10 +1705,8 @@ void QQuickTextInput::setPasswordCharacter(const QString &str)
if (str.length() < 1)
return;
d->m_passwordCharacter = str.constData()[0];
- d->updateDisplayText();
- if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit) {
- updateSize();
- }
+ if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit)
+ d->updateDisplayText();
emit passwordCharacterChanged();
}
@@ -1883,8 +2032,10 @@ void QQuickTextInputPrivate::init()
q, SLOT(q_canPasteChanged()));
canPaste = !m_readOnly && QGuiApplication::clipboard()->text().length() != 0;
#endif // QT_NO_CLIPBOARD
- updateDisplayText();
- q->updateSize();
+ m_textLayout.beginLayout();
+ m_textLayout.createLine();
+ m_textLayout.endLayout();
+
imHints &= ~Qt::ImhMultiLine;
oldValidity = hasAcceptableInput(m_text);
lastSelectionStart = 0;
@@ -1903,13 +2054,19 @@ void QQuickTextInputPrivate::init()
void QQuickTextInput::updateCursorRectangle()
{
Q_D(QQuickTextInput);
- d->determineHorizontalAlignment();
+ if (!isComponentComplete())
+ return;
+
d->updateHorizontalScroll();
- updateRect();//TODO: Only update rect between pos's
+ d->updateVerticalScroll();
+ update();
updateMicroFocus();
emit cursorRectangleChanged();
- if (d->cursorItem)
- d->cursorItem->setX(d->cursorToX() - d->hscroll);
+ if (d->cursorItem) {
+ QRectF r = cursorRectangle();
+ d->cursorItem->setPos(r.topLeft());
+ d->cursorItem->setHeight(r.height());
+ }
}
void QQuickTextInput::selectionChanged()
@@ -1932,21 +2089,6 @@ void QQuickTextInput::selectionChanged()
}
}
-void QQuickTextInput::q_textChanged()
-{
- Q_D(QQuickTextInput);
- emit textChanged();
- emit displayTextChanged();
- updateSize();
- d->determineHorizontalAlignment();
- d->updateHorizontalScroll();
- updateMicroFocus();
- if (hasAcceptableInput() != d->oldValidity) {
- d->oldValidity = hasAcceptableInput();
- emit acceptableInputChanged();
- }
-}
-
void QQuickTextInputPrivate::showCursor()
{
if (textNode != 0 && textNode->cursorNode() != 0)
@@ -1975,26 +2117,17 @@ void QQuickTextInput::updateRect(const QRect &r)
QRectF QQuickTextInput::boundingRect() const
{
Q_D(const QQuickTextInput);
- QRectF r = QQuickImplicitSizeItem::boundingRect();
+ QRectF r = d->boundingRect;
int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->m_cursorWidth;
// Could include font max left/right bearings to either side of rectangle.
r.setRight(r.right() + cursorWidth);
+ r.translate(-d->hscroll, -d->vscroll);
return r;
}
-void QQuickTextInput::updateSize(bool needsRedraw)
-{
- Q_D(QQuickTextInput);
- int w = width();
- int h = height();
- setImplicitSize(d->calculateTextWidth(), d->calculateTextHeight());
- if (w==width() && h==height() && needsRedraw)
- update();
-}
-
void QQuickTextInput::q_canPasteChanged()
{
Q_D(QQuickTextInput);
@@ -2041,20 +2174,53 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
uc[i] = QChar(0x0020);
}
- m_textLayout.setText(str);
+ if (str != orig || forceUpdate) {
+ m_textLayout.setText(str);
+ updateLayout(); // polish?
+ emit q_func()->displayTextChanged();
+ }
+}
+
+void QQuickTextInputPrivate::updateLayout()
+{
+ Q_Q(QQuickTextInput);
+
+ if (!q->isComponentComplete())
+ return;
QTextOption option = m_textLayout.textOption();
option.setTextDirection(m_layoutDirection);
option.setFlags(QTextOption::IncludeTrailingSpaces);
+ 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 l = m_textLayout.createLine();
+ QTextLine line = m_textLayout.createLine();
+ qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
+ qreal height = 0;
+ QTextLine firstLine = line;
+ do {
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(line.position().x(), height));
+ boundingRect = boundingRect.united(line.naturalTextRect());
+
+ height += line.height();
+ line = m_textLayout.createLine();
+ } while (line.isValid());
m_textLayout.endLayout();
- m_ascent = qRound(l.ascent());
- if (str != orig || forceUpdate)
- emit q_func()->displayTextChanged();
+ option.setWrapMode(QTextOption::NoWrap);
+ m_textLayout.setTextOption(option);
+
+ m_ascent = qRound(firstLine.ascent());
+ textLayoutDirty = true;
+
+ q->update();
+ q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
+
}
#ifndef QT_NO_CLIPBOARD
@@ -2117,7 +2283,7 @@ void QQuickTextInputPrivate::commitPreedit()
m_preeditCursor = 0;
m_textLayout.setPreeditArea(-1, QString());
m_textLayout.clearAdditionalFormats();
- updateDisplayText(/*force*/ true);
+ updateLayout();
}
/*!
@@ -2278,21 +2444,6 @@ void QQuickTextInputPrivate::updatePasswordEchoEditing(bool editing)
/*!
\internal
- Returns the cursor position of the given \a x pixel value in relation
- to the displayed text. The given \a betweenOrOn specified what kind
- of cursor position is requested.
-*/
-int QQuickTextInputPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
-{
- Q_Q(const QQuickTextInput);
- QRect cr = q->boundingRect().toRect();
- x-= cr.x() - hscroll;
- return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn);
-}
-
-/*!
- \internal
-
Fixes the current text so that it is valid given any set validators.
Returns true if the text was changed. Otherwise returns false.
@@ -2340,7 +2491,6 @@ void QQuickTextInputPrivate::moveCursor(int pos, bool mark)
anchor = m_cursor;
m_selstart = qMin(anchor, pos);
m_selend = qMax(anchor, pos);
- updateDisplayText();
} else {
internalDeselect();
}
@@ -2368,6 +2518,7 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
|| event->replacementLength() > 0;
bool cursorPositionChanged = false;
bool selectionChange = false;
+ m_preeditDirty = event->preeditString() != preeditAreaText();
if (isGettingInput) {
// If any text is being input, remove selected text.
@@ -2442,6 +2593,7 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
}
}
m_textLayout.setAdditionalFormats(formats);
+
updateDisplayText(/*force*/ true);
if (cursorPositionChanged)
emitCursorPositionChanged();
@@ -2542,8 +2694,17 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
if (m_textDirty) {
m_textDirty = false;
- q_func()->q_textChanged();
+ m_preeditDirty = false;
+ determineHorizontalAlignment();
+ emit q->textChanged();
}
+
+ if (m_validInput != wasValidInput)
+ emit q->acceptableInputChanged();
+ }
+ if (m_preeditDirty) {
+ m_preeditDirty = false;
+ determineHorizontalAlignment();
}
if (m_selDirty) {
m_selDirty = false;
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 7a07de60a3..447f3330ec 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -44,6 +44,7 @@
#define QQUICKTEXTINPUT_P_H
#include "qquickimplicitsizeitem_p.h"
+#include <QtGui/qtextoption.h>
#include <QtGui/qvalidator.h>
QT_BEGIN_HEADER
@@ -56,8 +57,11 @@ class Q_AUTOTEST_EXPORT QQuickTextInput : public QQuickImplicitSizeItem
{
Q_OBJECT
Q_ENUMS(HAlignment)
+ Q_ENUMS(VAlignment)
+ Q_ENUMS(WrapMode)
Q_ENUMS(EchoMode)
Q_ENUMS(SelectionMode)
+ Q_ENUMS(CursorPosition)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
@@ -66,6 +70,8 @@ class Q_AUTOTEST_EXPORT QQuickTextInput : public QQuickImplicitSizeItem
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged)
Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged)
+ Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged)
+ Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged)
Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged)
@@ -98,6 +104,8 @@ public:
QQuickTextInput(QQuickItem * parent=0);
~QQuickTextInput();
+ void componentComplete();
+
enum EchoMode {//To match QLineEdit::EchoMode
Normal,
NoEcho,
@@ -111,6 +119,20 @@ public:
AlignHCenter = Qt::AlignHCenter
};
+ enum VAlignment {
+ AlignTop = Qt::AlignTop,
+ AlignBottom = Qt::AlignBottom,
+ AlignVCenter = Qt::AlignVCenter
+ };
+
+ enum WrapMode {
+ NoWrap = QTextOption::NoWrap,
+ WordWrap = QTextOption::WordWrap,
+ WrapAnywhere = QTextOption::WrapAnywhere,
+ WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT
+ Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere
+ };
+
enum SelectionMode {
SelectCharacters,
SelectWords
@@ -121,9 +143,9 @@ public:
CursorOnCharacter
};
+
//Auxilliary functions needed to control the TextInput from QML
- Q_INVOKABLE int positionAt(int x) const;
- Q_INVOKABLE int positionAt(int x, CursorPosition position) const;
+ Q_INVOKABLE void positionAt(QDeclarativeV8Function *args) const;
Q_INVOKABLE QRectF positionToRectangle(int pos) const;
Q_INVOKABLE void moveCursorSelection(int pos);
Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode);
@@ -151,6 +173,12 @@ public:
void resetHAlign();
HAlignment effectiveHAlign() const;
+ VAlignment vAlign() const;
+ void setVAlign(VAlignment align);
+
+ WrapMode wrapMode() const;
+ void setWrapMode(WrapMode w);
+
bool isReadOnly() const;
void setReadOnly(bool);
@@ -226,6 +254,8 @@ Q_SIGNALS:
void selectedTextColorChanged(const QColor &color);
void fontChanged(const QFont &font);
void horizontalAlignmentChanged(HAlignment alignment);
+ void verticalAlignmentChanged(VAlignment alignment);
+ void wrapModeChanged();
void readOnlyChanged(bool isReadOnly);
void cursorVisibleChanged(bool isCursorVisible);
void cursorDelegateChanged();
@@ -273,8 +303,6 @@ public Q_SLOTS:
#endif
private Q_SLOTS:
- void updateSize(bool needsRedraw = true);
- void q_textChanged();
void selectionChanged();
void createCursor();
void updateCursorRectangle();
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index b410bfd187..a5fa6d58b8 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -81,7 +81,7 @@ public:
, textNode(0)
, m_maskData(0)
, hscroll(0)
- , oldScroll(0)
+ , vscroll(0)
, m_cursor(0)
, m_preeditCursor(0)
, m_cursorWidth(1)
@@ -97,6 +97,8 @@ public:
, m_selend(0)
, style(QQuickText::Normal)
, hAlign(QQuickTextInput::AlignLeft)
+ , vAlign(QQuickTextInput::AlignTop)
+ , wrapMode(QQuickTextInput::NoWrap)
, mouseSelectionMode(QQuickTextInput::SelectCharacters)
, inputMethodHints(Qt::ImhNone)
, m_layoutDirection(Qt::LayoutDirectionAuto)
@@ -116,6 +118,7 @@ public:
, m_readOnly(0)
, m_echoMode(QQuickTextInput::Normal)
, m_textDirty(0)
+ , m_preeditDirty(0)
, m_selDirty(0)
, m_validInput(1)
, m_blinkStatus(0)
@@ -130,6 +133,7 @@ public:
void init();
void startCreatingCursor();
void updateHorizontalScroll();
+ void updateVerticalScroll();
bool determineHorizontalAlignment();
bool setHAlign(QQuickTextInput::HAlignment, bool forceAlign = false);
void mirrorChange();
@@ -186,13 +190,14 @@ public:
QPoint tripleClickStartPoint;
QList<int> m_transactions;
QVector<Command> m_history;
+ QRectF boundingRect;
int lastSelectionStart;
int lastSelectionEnd;
int oldHeight;
int oldWidth;
int hscroll;
- int oldScroll;
+ int vscroll;
int m_cursor;
int m_preeditCursor;
int m_cursorWidth;
@@ -209,6 +214,8 @@ public:
QQuickText::TextStyle style;
QQuickTextInput::HAlignment hAlign;
+ QQuickTextInput::VAlignment vAlign;
+ QQuickTextInput::WrapMode wrapMode;
QQuickTextInput::SelectionMode mouseSelectionMode;
Qt::InputMethodHints inputMethodHints;
Qt::LayoutDirection m_layoutDirection;
@@ -232,6 +239,7 @@ public:
uint m_readOnly : 1;
uint m_echoMode : 2;
uint m_textDirty : 1;
+ uint m_preeditDirty : 1;
uint m_selDirty : 1;
uint m_validInput : 1;
uint m_blinkStatus : 1;
@@ -269,10 +277,6 @@ public:
bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); }
bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; }
- int calculateTextHeight() const { return qRound(m_textLayout.lineAt(0).height()); }
- int calculateTextWidth() const { return qRound(m_textLayout.lineAt(0).naturalTextWidth()); }
- int ascent() const { return m_ascent; }
-
void setSelection(int start, int length);
inline QString selectedText() const { return hasSelectedText() ? m_text.mid(m_selstart, m_selend - m_selstart) : QString(); }
@@ -281,12 +285,10 @@ public:
int selectionStart() const { return hasSelectedText() ? m_selstart : -1; }
int selectionEnd() const { return hasSelectedText() ? m_selend : -1; }
- bool inSelection(int x) const
- {
- if (m_selstart >= m_selend)
- return false;
- int pos = xToPos(x, QTextLine::CursorOnCharacter);
- return pos >= m_selstart && pos < m_selend;
+
+ int positionAt(int x, int y, QTextLine::CursorPosition position) const;
+ int positionAt(const QPointF &point, QTextLine::CursorPosition position = QTextLine::CursorBetweenCharacters) const {
+ return positionAt(point.x(), point.y(), position);
}
void removeSelection()
@@ -333,17 +335,6 @@ public:
void home(bool mark) { moveCursor(0, mark); }
void end(bool mark) { moveCursor(q_func()->text().length(), mark); }
- int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const;
-
- qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); }
- qreal cursorToX() const
- {
- int cursor = m_cursor;
- if (m_preeditCursor != -1)
- cursor += m_preeditCursor;
- return cursorToX(cursor);
- }
-
void backspace();
void del();
void deselect() { internalDeselect(); finishChange(); }
@@ -398,6 +389,8 @@ public:
void setCursorBlinkPeriod(int msec);
void resetCursorBlinkTimer();
+ void updateLayout();
+
private:
void init(const QString &txt);
void removeSelectedText();
@@ -425,7 +418,6 @@ private:
inline void separate() { m_separator = true; }
-
// masking
void parseInputMask(const QString &maskFields);
bool isValidInput(QChar key, QChar mask) const;
diff --git a/tests/auto/qtquick2/qquicktextinput/data/horizontalAlignment.qml b/tests/auto/qtquick2/qquicktextinput/data/horizontalAlignment.qml
index e0fef4c11e..89934532e3 100644
--- a/tests/auto/qtquick2/qquicktextinput/data/horizontalAlignment.qml
+++ b/tests/auto/qtquick2/qquicktextinput/data/horizontalAlignment.qml
@@ -10,10 +10,11 @@ Rectangle {
Rectangle {
anchors.centerIn: parent
width: 60
- height: 20
+ height: 60
color: "green"
TextInput {
+ objectName: "text"
id: text
anchors.fill: parent
text: top.text
diff --git a/tests/auto/qtquick2/qquicktextinput/data/positionAt.qml b/tests/auto/qtquick2/qquicktextinput/data/positionAt.qml
index 1840462c87..edb4744107 100644
--- a/tests/auto/qtquick2/qquicktextinput/data/positionAt.qml
+++ b/tests/auto/qtquick2/qquicktextinput/data/positionAt.qml
@@ -4,5 +4,6 @@ TextInput{
focus: true
objectName: "myInput"
width: 50
+ height: 100
text: "AAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}
diff --git a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
index 6b6fd73b01..a626cb2929 100644
--- a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
@@ -43,6 +43,7 @@
#include "../../shared/util.h"
#include <private/qinputpanel_p.h>
#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
#include <QFile>
#include <QtQuick/qquickview.h>
#include <QtGui/qguiapplication.h>
@@ -83,6 +84,15 @@ QString createExpectedFileIfNotFound(const QString& filebasename, const QImage&
return expectfile;
}
+template <typename T> static T evaluate(QObject *scope, const QString &expression)
+{
+ QDeclarativeExpression expr(qmlContext(scope), scope, expression);
+ T result = expr.evaluate().value<T>();
+ if (expr.hasError())
+ qWarning() << expr.error().toString();
+ return result;
+}
+
typedef QPair<int, QChar> Key;
class tst_qquicktextinput : public QObject
@@ -100,6 +110,7 @@ private slots:
void width();
void font();
void color();
+ void wrap();
void selection();
void isRightToLeft_data();
void isRightToLeft();
@@ -115,6 +126,7 @@ private slots:
void horizontalAlignment_data();
void horizontalAlignment();
void horizontalAlignment_RightToLeft();
+ void verticalAlignment();
void positionAt();
@@ -479,6 +491,41 @@ void tst_qquicktextinput::color()
}
}
+void tst_qquicktextinput::wrap()
+{
+ int textHeight = 0;
+ // for specified width and wrap set true
+ {
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
+ QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
+ textHeight = textObject->height();
+
+ QVERIFY(textObject != 0);
+ QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
+ QCOMPARE(textObject->width(), 300.);
+
+ delete textObject;
+ }
+
+ for (int i = 0; i < standard.count(); i++) {
+ QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
+ QDeclarativeComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE(textObject->width(), 30.);
+ QVERIFY(textObject->height() > textHeight);
+
+ int oldHeight = textObject->height();
+ textObject->setWidth(100);
+ QVERIFY(textObject->height() < oldHeight);
+
+ delete textObject;
+ }
+}
+
void tst_qquicktextinput::selection()
{
QString testStr = standard[0];
@@ -1178,37 +1225,37 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
QVERIFY(textInputPrivate != 0);
- QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
// implicit alignment should follow the reading direction of RTL text
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
- QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
// explicitly left aligned
textInput->setHAlign(QQuickTextInput::AlignLeft);
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
- QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
// explicitly right aligned
textInput->setHAlign(QQuickTextInput::AlignRight);
QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
- QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
// explicitly center aligned
textInput->setHAlign(QQuickTextInput::AlignHCenter);
QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
- QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
- QVERIFY(-textInputPrivate->hscroll + textInputPrivate->width > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
+ QVERIFY(textInput->boundingRect().right() > canvas.width()/2);
// reseted alignment should go back to following the text reading direction
textInput->resetHAlign();
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
- QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
// mirror the text item
QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
@@ -1216,19 +1263,19 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
// mirrored implicit alignment should continue to follow the reading direction of the text
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
- QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
// explicitly right aligned behaves as left aligned
textInput->setHAlign(QQuickTextInput::AlignRight);
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
- QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
// mirrored explicitly left aligned behaves as right aligned
textInput->setHAlign(QQuickTextInput::AlignLeft);
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
- QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
// disable mirroring
QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
@@ -1238,7 +1285,7 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
// English text should be implicitly left aligned
textInput->setText("Hello world!");
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
- QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
canvas.requestActivateWindow();
QTest::qWaitForWindowShown(&canvas);
@@ -1261,12 +1308,12 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
QCOMPARE(textInput->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
- QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
else
- QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
textInput->setHAlign(QQuickTextInput::AlignRight);
QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
- QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
+ QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
QString componentStr = "import QtQuick 2.0\nTextInput {}";
QDeclarativeComponent textComponent(&engine);
@@ -1277,6 +1324,31 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
delete textObject;
}
+void tst_qquicktextinput::verticalAlignment()
+{
+ QQuickView canvas(QUrl::fromLocalFile(TESTDATA("horizontalAlignment.qml")));
+ QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
+ QVERIFY(textInput != 0);
+ canvas.show();
+
+ QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
+ QVERIFY(textInputPrivate != 0);
+
+ QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
+ QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2);
+
+ // bottom aligned
+ textInput->setVAlign(QQuickTextInput::AlignBottom);
+ QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
+ QVERIFY(textInput->boundingRect().top () > canvas.height() / 2);
+
+ // explicitly center aligned
+ textInput->setVAlign(QQuickTextInput::AlignVCenter);
+ QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
+ QVERIFY(textInput->boundingRect().top() < canvas.height() / 2);
+ QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2);
+}
+
void tst_qquicktextinput::positionAt()
{
QQuickView canvas(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
@@ -1290,7 +1362,7 @@ void tst_qquicktextinput::positionAt()
// Check autoscrolled...
- int pos = textinputObject->positionAt(textinputObject->width()/2);
+ int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
QTextLayout layout(textinputObject->text());
layout.setFont(textinputObject->font());
@@ -1312,12 +1384,12 @@ void tst_qquicktextinput::positionAt()
QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
- QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
- QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
// Check without autoscroll...
textinputObject->setAutoScroll(false);
- pos = textinputObject->positionAt(textinputObject->width()/2);
+ pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
textLeftWidthBegin = floor(line.cursorToX(pos - 1));
textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
@@ -1326,8 +1398,8 @@ void tst_qquicktextinput::positionAt()
QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
x = textinputObject->positionToRectangle(pos + 1).x() - 1;
- QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
- QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
const qreal x0 = textinputObject->positionToRectangle(pos).x();
const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
@@ -1336,17 +1408,33 @@ void tst_qquicktextinput::positionAt()
textinputObject->setText(textinputObject->text().mid(pos));
textinputObject->setCursorPosition(0);
- QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
- QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent);
+ { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
+ QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
// Check all points within the preedit text return the same position.
- QCOMPARE(textinputObject->positionAt(0), 0);
- QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
- QCOMPARE(textinputObject->positionAt(x0), 0);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
// Verify positioning returns to normal after the preedit text.
- QCOMPARE(textinputObject->positionAt(x1), 1);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
+
+ { QInputMethodEvent inputEvent;
+ QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
+
+ // With wrapping.
+ textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
+
+ const qreal y0 = line.height() / 2;
+ const qreal y1 = line.height() * 3 / 2;
+
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
+
+ int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
+ QVERIFY(newLinePos > pos);
+ QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
}
void tst_qquicktextinput::maxLength()
@@ -1962,6 +2050,7 @@ void tst_qquicktextinput::cursorRectangle()
layout.endLayout();
input.setWidth(line.cursorToX(5, QTextLine::Leading));
+ input.setHeight(qCeil(line.height() * 3 / 2));
QRect r;
@@ -1982,7 +2071,7 @@ void tst_qquicktextinput::cursorRectangle()
}
// Check the cursor rectangle remains within the input bounding rect when auto scrolling.
- QVERIFY(r.left() < input.boundingRect().width());
+ QVERIFY(r.left() < input.width());
QVERIFY(r.right() >= input.width() - error);
for (int i = 6; i < text.length(); ++i) {
@@ -1994,14 +2083,50 @@ void tst_qquicktextinput::cursorRectangle()
for (int i = text.length() - 2; i >= 0; --i) {
input.setCursorPosition(i);
r = input.cursorRectangle();
+ QCOMPARE(r.top(), 0);
QVERIFY(r.right() >= 0);
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
}
+ // Check vertical scrolling with word wrap.
+ input.setWrapMode(QQuickTextInput::WordWrap);
+ for (int i = 0; i <= 5; ++i) {
+ input.setCursorPosition(i);
+ r = input.cursorRectangle();
+
+ QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
+ QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
+ QCOMPARE(r.top(), 0);
+ QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
+ }
+
+ input.setCursorPosition(6);
+ r = input.cursorRectangle();
+ QCOMPARE(r.left(), 0);
+ QVERIFY(r.bottom() >= input.height() - error);
+
+ for (int i = 7; i < text.length(); ++i) {
+ input.setCursorPosition(i);
+ r = input.cursorRectangle();
+ QVERIFY(r.bottom() >= input.height() - error);
+ }
+
+ for (int i = text.length() - 2; i >= 6; --i) {
+ input.setCursorPosition(i);
+ r = input.cursorRectangle();
+ QVERIFY(r.bottom() >= input.height() - error);
+ }
+
+ for (int i = 5; i >= 0; --i) {
+ input.setCursorPosition(i);
+ r = input.cursorRectangle();
+ QCOMPARE(r.top(), 0);
+ }
+
input.setText("Hi!");
input.setHAlign(QQuickTextInput::AlignRight);
r = input.cursorRectangle();
- QVERIFY(r.left() < input.boundingRect().width());
+ QVERIFY(r.left() < input.width() + error);
QVERIFY(r.right() >= input.width() - error);
}
@@ -2215,7 +2340,6 @@ void tst_qquicktextinput::openInputPanel()
// check default values
QVERIFY(input->focusOnPress());
QVERIFY(!input->hasActiveFocus());
- qDebug() << &input << qApp->inputPanel()->inputItem();
QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
QCOMPARE(qApp->inputPanel()->visible(), false);
@@ -2419,15 +2543,15 @@ void tst_qquicktextinput::preeditAutoScroll()
// test the text is scrolled so the preedit is visible.
sendPreeditText(preeditText.mid(0, 3), 1);
- QVERIFY(input->positionAt(0) != 0);
+ QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
// test the text is scrolled back when the preedit is removed.
QInputMethodEvent imEvent;
QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
- QCOMPARE(input->positionAt(0), 0);
- QCOMPARE(input->positionAt(input->width()), 5);
+ QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
+ QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
QTextLayout layout(preeditText);
@@ -2482,8 +2606,8 @@ void tst_qquicktextinput::preeditAutoScroll()
input->setAutoScroll(false);
sendPreeditText(preeditText.mid(0, 3), 1);
- QCOMPARE(input->positionAt(0), 0);
- QCOMPARE(input->positionAt(input->width()), 5);
+ QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
+ QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
}
void tst_qquicktextinput::preeditCursorRectangle()
diff --git a/tests/testapplications/text/textinput.qml b/tests/testapplications/text/textinput.qml
index 98f2628372..271466d59a 100644
--- a/tests/testapplications/text/textinput.qml
+++ b/tests/testapplications/text/textinput.qml
@@ -73,6 +73,8 @@ Rectangle {
font.pointSize: { pointvalue.model.get(pointvalue.currentIndex).value }
font.pixelSize: { pixelvalue.model.get(pixelvalue.currentIndex).value }
horizontalAlignment: { halignvalue.model.get(halignvalue.currentIndex).value }
+ verticalAlignment: { valignvalue.model.get(valignvalue.currentIndex).value }
+ wrapMode: { wrapvalue.model.get(wrapvalue.currentIndex).value }
smooth: { smoothvalue.model.get(smoothvalue.currentIndex).value }
selectByMouse: { mousevalue.model.get(mousevalue.currentIndex).value }
echoMode: { echovalue.model.get(echovalue.currentIndex).value }