Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Content size should not include trailing spaces.
Excluding trailing spaces from the content size means the cursor
position also needs to considered in determining the width used
by the text as unwrapped white space can push the cursor over
the width of the item.

Also corrects an auto scroll issue with right to left text
identified in extending the tests.

Task-number: QTBUG-24630

Change-Id: Iaab9eac03824b22f507154fa1d6e55376bd075a0
Reviewed-by: Yann Bodson <yann.bodson@nokia.com>
  • Loading branch information
Andrew den Exter authored and Qt by Nokia committed Mar 21, 2012
1 parent 122e8df commit 0e1baa2
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 42 deletions.
15 changes: 11 additions & 4 deletions src/quick/items/qquicktextinput.cpp
Expand Up @@ -1614,14 +1614,19 @@ void QQuickTextInputPrivate::updateHorizontalScroll()
QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor);
const int preeditLength = m_textLayout.preeditAreaText().length();
const qreal width = qMax<qreal>(0, q->width());
qreal widthUsed = currentLine.isValid() ? currentLine.naturalTextWidth() : 0;
qreal cix = 0;
qreal widthUsed = 0;
if (currentLine.isValid()) {
cix = currentLine.cursorToX(m_cursor + preeditLength);
const qreal cursorWidth = cix >= 0 ? cix : width - cix;
widthUsed = qMax(currentLine.naturalTextWidth(), cursorWidth);
}
int previousScroll = hscroll;

if (!autoScroll || widthUsed <= width || m_echoMode == QQuickTextInput::NoEcho) {
hscroll = 0;
} else {
Q_ASSERT(currentLine.isValid());
qreal cix = 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;
Expand All @@ -1632,6 +1637,10 @@ void QQuickTextInputPrivate::updateHorizontalScroll()
// text doesn't fit, text document is to the left of br; align
// right
hscroll = widthUsed - width;
} else if (width - hscroll > widthUsed) {
// text doesn't fit, text document is to the right of br; align
// left
hscroll = width - widthUsed;
}
if (preeditLength > 0) {
// check to ensure long pre-edit text doesn't push the cursor
Expand Down Expand Up @@ -2699,7 +2708,6 @@ void QQuickTextInputPrivate::updateLayout()

QTextOption option = m_textLayout.textOption();
option.setTextDirection(layoutDirection());
option.setFlags(QTextOption::IncludeTrailingSpaces);
option.setWrapMode(QTextOption::WrapMode(wrapMode));
option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
m_textLayout.setTextOption(option);
Expand All @@ -2710,7 +2718,6 @@ void QQuickTextInputPrivate::updateLayout()
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));
Expand Down
143 changes: 105 additions & 38 deletions tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
Expand Up @@ -93,6 +93,8 @@ template <typename T> static T evaluate(QObject *scope, const QString &expressio
return result;
}

template<typename T, int N> int lengthOf(const T (&)[N]) { return N; }

typedef QPair<int, QChar> Key;

class tst_qquicktextinput : public QQmlDataTest
Expand Down Expand Up @@ -140,6 +142,7 @@ private slots:
void cursorDelegate_data();
void cursorDelegate();
void cursorVisible();
void cursorRectangle_data();
void cursorRectangle();
void navigation();
void navigation_RTL();
Expand Down Expand Up @@ -2405,10 +2408,34 @@ void tst_qquicktextinput::cursorVisible()
QCOMPARE(spy.count(), 7);
}

void tst_qquicktextinput::cursorRectangle_data()
{
const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};

QTest::addColumn<QString>("text");
QTest::addColumn<int>("positionAtWidth");
QTest::addColumn<int>("wrapPosition");
QTest::addColumn<QString>("shortText");
QTest::addColumn<bool>("leftToRight");

QTest::newRow("left to right")
<< "Hello World!" << 5 << 11
<< "Hi"
<< true;
QTest::newRow("right to left")
<< QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
<< QString::fromUtf16(arabic_str, 3)
<< false;
}

void tst_qquicktextinput::cursorRectangle()
{

QString text = "Hello World!";
QFETCH(QString, text);
QFETCH(int, positionAtWidth);
QFETCH(int, wrapPosition);
QFETCH(QString, shortText);
QFETCH(bool, leftToRight);

QQuickTextInput input;
input.setText(text);
Expand All @@ -2425,33 +2452,30 @@ void tst_qquicktextinput::cursorRectangle()
QTextLine line = layout.createLine();
layout.endLayout();

input.setWidth(line.cursorToX(5, QTextLine::Leading));
qreal offset = 0;
if (leftToRight) {
input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
} else {
input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
offset = line.horizontalAdvance() - input.width();
}
input.setHeight(qCeil(line.height() * 3 / 2));

QRectF r;

// some tolerance for different fonts.
#ifdef Q_OS_LINUX
const int error = 2;
#else
const int error = 5;
#endif

for (int i = 0; i <= 5; ++i) {
for (int i = 0; i <= positionAtWidth; ++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.left(), line.cursorToX(i, QTextLine::Leading) - offset);
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

// Check the cursor rectangle remains within the input bounding rect when auto scrolling.
QVERIFY(r.left() < input.width() + error);
QVERIFY(r.right() >= input.width() - error);
QCOMPARE(r.left(), leftToRight ? input.width() : 0);

for (int i = 6; i < text.length(); ++i) {
for (int i = positionAtWidth + 1; i < text.length(); ++i) {
input.setCursorPosition(i);
QCOMPARE(r, input.cursorRectangle());
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
Expand All @@ -2462,89 +2486,125 @@ void tst_qquicktextinput::cursorRectangle()
input.setCursorPosition(i);
r = input.cursorRectangle();
QCOMPARE(r.top(), 0.);
if (leftToRight) {
QVERIFY(r.right() >= 0);
} else {
QVERIFY(r.left() <= input.width());
}
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

// Check position with word wrap.
input.setWrapMode(QQuickTextInput::WordWrap);
input.setAutoScroll(false);
for (int i = 0; i <= 5; ++i) {
for (int i = 0; i < wrapPosition; ++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)));
if (i > positionAtWidth)
QEXPECT_FAIL("right to left", "QTBUG-24801", Continue);
QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
QCOMPARE(r.top(), 0.);
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

input.setCursorPosition(6);
input.setCursorPosition(wrapPosition);
r = input.cursorRectangle();
if (leftToRight) {
QCOMPARE(r.left(), 0.);
QVERIFY(r.top() > line.height() - error);
} else {
QCOMPARE(r.left(), input.width());
}
QVERIFY(r.top() >= line.height() - 1);
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(6), r);
QCOMPARE(input.positionToRectangle(11), r);

for (int i = 7; i < text.length(); ++i) {
for (int i = wrapPosition + 1; i < text.length(); ++i) {
input.setCursorPosition(i);
r = input.cursorRectangle();
QVERIFY(r.top() > line.height() - error);
QVERIFY(r.top() >= line.height() - 1);
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

// Check vertical scrolling with word wrap.
input.setAutoScroll(true);
for (int i = 0; i <= 5; ++i) {
for (int i = 0; i <= positionAtWidth; ++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.left(), line.cursorToX(i, QTextLine::Leading) - offset);
QCOMPARE(r.top(), 0.);
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

input.setCursorPosition(6);
// Whitespace doesn't wrap, so scroll horizontally until the until the cursor
// reaches the next non-whitespace character.
QCOMPARE(r.left(), leftToRight ? input.width() : 0);
for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
input.setCursorPosition(i);
QCOMPARE(r, input.cursorRectangle());
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

input.setCursorPosition(wrapPosition);
r = input.cursorRectangle();
if (leftToRight) {
QCOMPARE(r.left(), 0.);
QVERIFY(r.bottom() >= input.height() - error);
} else {
QCOMPARE(r.left(), input.width());
}
QVERIFY(r.bottom() >= input.height());
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(6), r);
QCOMPARE(input.positionToRectangle(11), r);

for (int i = 7; i < text.length(); ++i) {
for (int i = wrapPosition + 1; i < text.length(); ++i) {
input.setCursorPosition(i);
r = input.cursorRectangle();
QVERIFY(r.bottom() >= input.height() - error);
QVERIFY(r.bottom() >= input.height());
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

for (int i = text.length() - 2; i >= 6; --i) {
for (int i = text.length() - 2; i >= wrapPosition; --i) {
input.setCursorPosition(i);
r = input.cursorRectangle();
QVERIFY(r.bottom() >= input.height() - error);
QVERIFY(r.bottom() >= input.height());
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

input.setCursorPosition(wrapPosition - 1);
r = input.cursorRectangle();
QCOMPARE(r.top(), 0.);
QEXPECT_FAIL("right to left", "QTBUG-24801", Continue);
QCOMPARE(r.left(), leftToRight ? input.width() : 0);
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(10), r);

for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
input.setCursorPosition(i);
QCOMPARE(r, input.cursorRectangle());
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

for (int i = 5; i >= 0; --i) {
for (int i = positionAtWidth; i >= 0; --i) {
input.setCursorPosition(i);
r = input.cursorRectangle();
QCOMPARE(r.top(), 0.);
QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
QCOMPARE(input.positionToRectangle(i), r);
}

input.setText("Hi!");
input.setHAlign(QQuickTextInput::AlignRight);
input.setText(shortText);
input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
r = input.cursorRectangle();
QVERIFY(r.left() < input.width() + error);
QVERIFY(r.right() >= input.width() - error);
QCOMPARE(r.left(), leftToRight ? input.width() : 0);
}

void tst_qquicktextinput::readOnly()
Expand Down Expand Up @@ -2891,6 +2951,13 @@ void tst_qquicktextinput::contentSize()
QVERIFY(textObject->contentWidth() > textObject->width());
QVERIFY(textObject->contentHeight() > textObject->height());
QCOMPARE(spy.count(), 3);

textObject->setText("The quick red fox jumped over the lazy brown dog");
for (int w = 60; w < 120; ++w) {
textObject->setWidth(w);
QVERIFY(textObject->contentWidth() <= textObject->width());
QVERIFY(textObject->contentHeight() > textObject->height());
}
}

static void sendPreeditText(const QString &text, int cursor)
Expand Down

0 comments on commit 0e1baa2

Please sign in to comment.