aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew den Exter <andrew.den-exter@nokia.com>2011-12-15 11:36:54 +1000
committerQt by Nokia <qt-info@nokia.com>2011-12-20 07:33:37 +0100
commit59e607a2ce1e1fbb5324040c26554ecc22d8b1fd (patch)
treee559c0f40407bd6a2ef0b50530bafa75e09abbdb
parentac50bb1b85ccd3e08053e1284be50847d32688dc (diff)
Add wrapMode and verticalAlignment properties to TextInput.
Wrap mode provides an alternative to horizontal scrolling when the width of the text exceeds the width of the TextInput. With auto scroll wrapping introdoces an implicit verticalAlignment so support setting it explicitly as well. Task-number: QTBUG-22305 Task-number: QTBUG-16203 Change-Id: I1bd3a5335edb3ac48df3d5ccd8ae7274caa91883 Reviewed-by: Martin Jones <martin.jones@nokia.com>
-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 }