From: Andrew den Exter Date: Thu, 19 Apr 2012 01:08:28 +0000 (+1000) Subject: Fix bounding rects of text items. X-Git-Tag: 071012131707~521 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2a6e105f43885e0901c252bffc77bd1c9db87b67;p=profile%2Fivi%2Fqtdeclarative.git Fix bounding rects of text items. Ensure the rectangles are correctly positioned with right and center aligned text, not just sized correctly. Also add padding to the clip rects so the cursor and styled text aren't clipped at the item boundaries. Change-Id: I03ef140589154ebd49b600b0a4c4fbeff845c10f Reviewed-by: Yann Bodson --- diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 2407ade..4d39f0b 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2039,6 +2039,16 @@ QRectF QQuickText::boundingRect() const return rect; } +QRectF QQuickText::clipRect() const +{ + Q_D(const QQuickText); + + QRectF rect = QQuickImplicitSizeItem::clipRect(); + if (d->style != Normal) + rect.adjust(-1, 0, 1, 2); + return rect; +} + /*! \internal */ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index e643d1d..e69159e 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -197,6 +197,7 @@ public: qreal contentHeight() const; QRectF boundingRect() const; + QRectF clipRect() const; Q_INVOKABLE void doLayout(); Q_SIGNALS: diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 59d78ac..cd5f262 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1818,7 +1818,48 @@ void QQuickTextEdit::updateSelectionMarkers() QRectF QQuickTextEdit::boundingRect() const { Q_D(const QQuickTextEdit); - QRectF r = QQuickImplicitSizeItem::boundingRect(); + QRectF r(0, -d->yoff, d->contentSize.width(), d->contentSize.height()); + int cursorWidth = 1; + if (d->cursor) + cursorWidth = 0; + else if (!d->document->isEmpty()) + cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor + + // Could include font max left/right bearings to either side of rectangle. + + r.setRight(r.right() + cursorWidth); + + qreal h = height(); + switch (d->vAlign) { + case AlignTop: + break; + case AlignBottom: + r.moveTop(h - r.height()); + break; + case AlignVCenter: + r.moveTop((h - r.height()) / 2); + break; + } + + qreal w = width(); + switch (d->hAlign) { + case AlignLeft: + break; + case AlignRight: + r.moveLeft(w - r.width()); + break; + case AlignHCenter: + r.moveLeft((w - r.width()) / 2); + break; + } + + return r; +} + +QRectF QQuickTextEdit::clipRect() const +{ + Q_D(const QQuickTextEdit); + QRectF r = QQuickImplicitSizeItem::clipRect(); int cursorWidth = 1; if (d->cursor) cursorWidth = d->cursor->width(); @@ -1828,7 +1869,7 @@ QRectF QQuickTextEdit::boundingRect() const // Could include font max left/right bearings to either side of rectangle. r.setRight(r.right() + cursorWidth); - return r.translated(0,d->yoff); + return r; } qreal QQuickTextEditPrivate::getImplicitWidth() const @@ -1903,7 +1944,7 @@ void QQuickTextEdit::updateSize() if (!widthValid() && !d->requireImplicitWidth) iWidth = newWidth; - qreal newHeight = d->document->isEmpty() ? fm.height() : d->document->size().height(); + qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height(); if (iWidth > -1) setImplicitSize(iWidth, newHeight); diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index b28ec9d..9f904ca 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -229,6 +229,7 @@ public: Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode); QRectF boundingRect() const; + QRectF clipRect() const; bool isInputMethodComposing() const; diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 8637294..c9019c6 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -1655,7 +1655,7 @@ void QQuickTextInputPrivate::updateVerticalScroll() Q_Q(QQuickTextInput); const int preeditLength = m_textLayout.preeditAreaText().length(); const qreal height = qMax(0, q->height()); - qreal heightUsed = boundingRect.height(); + qreal heightUsed = contentSize.height(); qreal previousScroll = vscroll; if (!autoScroll || heightUsed <= height) { @@ -2265,7 +2265,7 @@ bool QQuickTextInput::canRedo() const qreal QQuickTextInput::contentWidth() const { Q_D(const QQuickTextInput); - return d->boundingRect.width(); + return d->contentSize.width(); } /*! @@ -2278,7 +2278,7 @@ qreal QQuickTextInput::contentWidth() const qreal QQuickTextInput::contentHeight() const { Q_D(const QQuickTextInput); - return d->boundingRect.height(); + return d->contentSize.height(); } void QQuickTextInput::moveCursorSelection(int position) @@ -2597,10 +2597,36 @@ QRectF QQuickTextInput::boundingRect() const { Q_D(const QQuickTextInput); + int cursorWidth = d->cursorItem ? 0 : 1; + + qreal hscroll = d->hscroll; + if (!d->autoScroll || d->contentSize.width() < width()) { + switch (effectiveHAlign()) { + case AlignLeft: + break; + case AlignRight: + hscroll += d->contentSize.width() - width(); + break; + case AlignHCenter: + hscroll += (d->contentSize.width() - width()) / 2; + break; + } + } + + // Could include font max left/right bearings to either side of rectangle. + QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height()); + r.setRight(r.right() + cursorWidth); + return r; +} + +QRectF QQuickTextInput::clipRect() const +{ + Q_D(const QQuickTextInput); + int cursorWidth = d->cursorItem ? d->cursorItem->width() : 1; // Could include font max left/right bearings to either side of rectangle. - QRectF r = QQuickImplicitSizeItem::boundingRect(); + QRectF r = QQuickImplicitSizeItem::clipRect(); r.setRight(r.right() + cursorWidth); return r; } @@ -2727,7 +2753,6 @@ void QQuickTextInputPrivate::updateLayout() if (!q->isComponentComplete()) return; - const QRectF previousRect = boundingRect; QTextOption option = m_textLayout.textOption(); option.setTextDirection(layoutDirection()); @@ -2736,8 +2761,8 @@ void QQuickTextInputPrivate::updateLayout() m_textLayout.setTextOption(option); m_textLayout.setFont(font); - boundingRect = QRectF(); m_textLayout.beginLayout(); + QTextLine line = m_textLayout.createLine(); if (requireImplicitWidth) { line.setLineWidth(INT_MAX); @@ -2750,12 +2775,14 @@ void QQuickTextInputPrivate::updateLayout() } qreal lineWidth = q->widthValid() ? q->width() : INT_MAX; qreal height = 0; + qreal width = 0; do { line.setLineWidth(lineWidth); - line.setPosition(QPointF(line.position().x(), height)); - boundingRect = boundingRect.united(line.naturalTextRect()); + line.setPosition(QPointF(0, height)); height += line.height(); + width = qMax(width, line.naturalTextWidth()); + line = m_textLayout.createLine(); } while (line.isValid()); m_textLayout.endLayout(); @@ -2765,15 +2792,18 @@ void QQuickTextInputPrivate::updateLayout() textLayoutDirty = true; + const QSizeF previousSize = contentSize; + contentSize = QSizeF(width, height); + updateType = UpdatePaintNode; q->update(); if (!requireImplicitWidth && !q->widthValid()) - q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height())); + q->setImplicitSize(width, height); else - q->setImplicitHeight(qCeil(boundingRect.height())); + q->setImplicitHeight(height); - if (previousRect != boundingRect) + if (previousSize != contentSize) emit q->contentSizeChanged(); } diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 9d32eee..9a3be0f 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -245,6 +245,8 @@ public: QVariant inputMethodQuery(Qt::InputMethodQuery property) const; QRectF boundingRect() const; + QRectF clipRect() const; + #ifndef QT_NO_CLIPBOARD bool canPaste() const; #endif diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 1bc2cf5..d952b27 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -172,7 +172,7 @@ public: }; QElapsedTimer tripleClickTimer; - QRectF boundingRect; + QSizeF contentSize; QPointF pressPos; QPointF tripleClickStartPoint; diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 297fa01..d1899a6 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -114,6 +114,7 @@ private slots: void boundingRect_data(); void boundingRect(); + void clipRect(); void lineLaidOut(); void imgTagsBaseUrl_data(); @@ -1802,6 +1803,50 @@ void tst_qquicktext::boundingRect() QVERIFY(text->boundingRect().height() > line.height()); } +void tst_qquicktext::clipRect() +{ + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\n Text {}", QUrl()); + QScopedPointer object(component.create()); + QQuickText *text = qobject_cast(object.data()); + QVERIFY(text); + + QTextLayout layout; + layout.setFont(text->font()); + + QCOMPARE(text->clipRect().x(), qreal(0)); + QCOMPARE(text->clipRect().y(), qreal(0)); + QCOMPARE(text->clipRect().width(), text->width()); + QCOMPARE(text->clipRect().height(), text->height()); + + text->setText("Hello World"); + + QCOMPARE(text->clipRect().x(), qreal(0)); + QCOMPARE(text->clipRect().y(), qreal(0)); + QCOMPARE(text->clipRect().width(), text->width()); + QCOMPARE(text->clipRect().height(), text->height()); + + // Clip rect follows the item not content dimensions. + text->setWidth(text->width() / 2); + QCOMPARE(text->clipRect().x(), qreal(0)); + QCOMPARE(text->clipRect().y(), qreal(0)); + QCOMPARE(text->clipRect().width(), text->width()); + QCOMPARE(text->clipRect().height(), text->height()); + + text->setHeight(text->height() * 2); + QCOMPARE(text->clipRect().x(), qreal(0)); + QCOMPARE(text->clipRect().y(), qreal(0)); + QCOMPARE(text->clipRect().width(), text->width()); + QCOMPARE(text->clipRect().height(), text->height()); + + // Setting a style adds a small amount of padding to the clip rect. + text->setStyle(QQuickText::Outline); + QCOMPARE(text->clipRect().x(), qreal(-1)); + QCOMPARE(text->clipRect().y(), qreal(0)); + QCOMPARE(text->clipRect().width(), text->width() + 2); + QCOMPARE(text->clipRect().height(), text->height() + 2); +} + void tst_qquicktext::lineLaidOut() { QQuickView *canvas = createView(testFile("lineLayout.qml")); diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 1a5f52f..9c34a7c 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -152,6 +152,8 @@ private slots: void implicitSize_data(); void implicitSize(); void contentSize(); + void boundingRect(); + void clipRect(); void implicitSizeBinding_data(); void implicitSizeBinding(); @@ -2657,6 +2659,152 @@ void tst_qquicktextedit::implicitSizeBinding() QCOMPARE(textObject->height(), textObject->implicitHeight()); } +void tst_qquicktextedit::clipRect() +{ + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\n TextEdit {}", QUrl()); + QScopedPointer object(component.create()); + QQuickTextEdit *edit = qobject_cast(object.data()); + QVERIFY(edit); + + QCOMPARE(edit->clipRect().x(), qreal(0)); + QCOMPARE(edit->clipRect().y(), qreal(0)); + + QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width()); + QCOMPARE(edit->clipRect().height(), edit->height()); + + edit->setText("Hello World"); + QCOMPARE(edit->clipRect().x(), qreal(0)); + QCOMPARE(edit->clipRect().y(), qreal(0)); + // XXX: TextEdit allows an extra 3 pixels boundary for the cursor beyond it's width for non + // empty text. TextInput doesn't. + QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3); + QCOMPARE(edit->clipRect().height(), edit->height()); + + // clip rect shouldn't exceed the size of the item, expect for the cursor width; + edit->setWidth(edit->width() / 2); + QCOMPARE(edit->clipRect().x(), qreal(0)); + QCOMPARE(edit->clipRect().y(), qreal(0)); + QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3); + QCOMPARE(edit->clipRect().height(), edit->height()); + + edit->setHeight(edit->height() * 2); + QCOMPARE(edit->clipRect().x(), qreal(0)); + QCOMPARE(edit->clipRect().y(), qreal(0)); + QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3); + QCOMPARE(edit->clipRect().height(), edit->height()); + + QQmlComponent cursorComponent(&engine); + cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl()); + + edit->setCursorDelegate(&cursorComponent); + + // If a cursor delegate is used it's size should determine the excess width. + QCOMPARE(edit->clipRect().x(), qreal(0)); + QCOMPARE(edit->clipRect().y(), qreal(0)); + QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3); + QCOMPARE(edit->clipRect().height(), edit->height()); + + // Alignment and wrapping don't affect the clip rect. + edit->setHAlign(QQuickTextEdit::AlignRight); + QCOMPARE(edit->clipRect().x(), qreal(0)); + QCOMPARE(edit->clipRect().y(), qreal(0)); + QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3); + QCOMPARE(edit->clipRect().height(), edit->height()); + + edit->setWrapMode(QQuickTextEdit::Wrap); + QCOMPARE(edit->clipRect().x(), qreal(0)); + QCOMPARE(edit->clipRect().y(), qreal(0)); + QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3); + QCOMPARE(edit->clipRect().height(), edit->height()); + + edit->setVAlign(QQuickTextEdit::AlignBottom); + QCOMPARE(edit->clipRect().x(), qreal(0)); + QCOMPARE(edit->clipRect().y(), qreal(0)); + QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3); + QCOMPARE(edit->clipRect().height(), edit->height()); +} + +void tst_qquicktextedit::boundingRect() +{ + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\n TextEdit {}", QUrl()); + QScopedPointer object(component.create()); + QQuickTextEdit *edit = qobject_cast(object.data()); + QVERIFY(edit); + + QTextLayout layout; + layout.setFont(edit->font()); + + if (!qmlDisableDistanceField()) { + QTextOption option; + option.setUseDesignMetrics(true); + layout.setTextOption(option); + } + layout.beginLayout(); + QTextLine line = layout.createLine(); + layout.endLayout(); + + QCOMPARE(edit->boundingRect().x(), qreal(0)); + QCOMPARE(edit->boundingRect().y(), qreal(0)); + QCOMPARE(edit->boundingRect().width(), edit->cursorRectangle().width()); + QCOMPARE(edit->boundingRect().height(), line.height()); + + edit->setText("Hello World"); + + layout.setText(edit->text()); + layout.beginLayout(); + line = layout.createLine(); + layout.endLayout(); + + QCOMPARE(edit->boundingRect().x(), qreal(0)); + QCOMPARE(edit->boundingRect().y(), qreal(0)); + QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3); + QCOMPARE(edit->boundingRect().height(), line.height()); + + // the size of the bounding rect shouldn't be bounded by the size of item. + edit->setWidth(edit->width() / 2); + QCOMPARE(edit->boundingRect().x(), qreal(0)); + QCOMPARE(edit->boundingRect().y(), qreal(0)); + QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3); + QCOMPARE(edit->boundingRect().height(), line.height()); + + edit->setHeight(edit->height() * 2); + QCOMPARE(edit->boundingRect().x(), qreal(0)); + QCOMPARE(edit->boundingRect().y(), qreal(0)); + QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3); + QCOMPARE(edit->boundingRect().height(), line.height()); + + QQmlComponent cursorComponent(&engine); + cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl()); + + edit->setCursorDelegate(&cursorComponent); + + // Don't include the size of a cursor delegate as it has its own bounding rect. + QCOMPARE(edit->boundingRect().x(), qreal(0)); + QCOMPARE(edit->boundingRect().y(), qreal(0)); + QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth()); + QCOMPARE(edit->boundingRect().height(), line.height()); + + edit->setHAlign(QQuickTextEdit::AlignRight); + QCOMPARE(edit->boundingRect().x(), edit->width() - line.naturalTextWidth()); + QCOMPARE(edit->boundingRect().y(), qreal(0)); + QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth()); + QCOMPARE(edit->boundingRect().height(), line.height()); + + edit->setWrapMode(QQuickTextEdit::Wrap); + QCOMPARE(edit->boundingRect().right(), edit->width()); + QCOMPARE(edit->boundingRect().y(), qreal(0)); + QVERIFY(edit->boundingRect().width() < line.naturalTextWidth()); + QVERIFY(edit->boundingRect().height() > line.height()); + + edit->setVAlign(QQuickTextEdit::AlignBottom); + QCOMPARE(edit->boundingRect().right(), edit->width()); + QCOMPARE(edit->boundingRect().bottom(), edit->height()); + QVERIFY(edit->boundingRect().width() < line.naturalTextWidth()); + QVERIFY(edit->boundingRect().height() > line.height()); +} + void tst_qquicktextedit::preeditCursorRectangle() { QString preeditText = "super"; diff --git a/tests/auto/quick/qquicktextinput/data/horizontalAlignment_RightToLeft.qml b/tests/auto/quick/qquicktextinput/data/horizontalAlignment_RightToLeft.qml index 5f88025..d14caea 100644 --- a/tests/auto/quick/qquicktextinput/data/horizontalAlignment_RightToLeft.qml +++ b/tests/auto/quick/qquicktextinput/data/horizontalAlignment_RightToLeft.qml @@ -19,6 +19,8 @@ Rectangle { anchors.fill: parent text: top.text focus: true + + cursorDelegate: Rectangle { } } } } diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 6f75698..27e557b 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -129,6 +129,7 @@ private slots: void horizontalAlignment_RightToLeft(); void verticalAlignment(); + void clipRect(); void boundingRect(); void positionAt(); @@ -1309,43 +1310,41 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft() const QString rtlText = textInput->text(); - QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput); - QVERIFY(textInputPrivate != 0); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); // implicit alignment should follow the reading direction of RTL text QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight); QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); // explicitly left aligned textInput->setHAlign(QQuickTextInput::AlignLeft); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft); QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); - QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0)); + QCOMPARE(textInput->boundingRect().left(), qreal(0)); // explicitly right aligned textInput->setHAlign(QQuickTextInput::AlignRight); QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); // explicitly center aligned textInput->setHAlign(QQuickTextInput::AlignHCenter); QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter); - QVERIFY(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll > 0); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll < textInput->width()); + QVERIFY(textInput->boundingRect().left() > 0); + QVERIFY(textInput->boundingRect().right() < textInput->width()); // 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->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); // mirror the text item QQuickItemPrivate::get(textInput)->setLayoutMirror(true); @@ -1353,21 +1352,21 @@ 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->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); // explicitly right aligned behaves as left aligned textInput->setHAlign(QQuickTextInput::AlignRight); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight); QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft); - QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0)); + QCOMPARE(textInput->boundingRect().left(), qreal(0)); // mirrored explicitly left aligned behaves as right aligned textInput->setHAlign(QQuickTextInput::AlignLeft); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft); QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); // disable mirroring QQuickItemPrivate::get(textInput)->setLayoutMirror(false); @@ -1377,7 +1376,7 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft() // English text should be implicitly left aligned textInput->setText("Hello world!"); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft); - QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0)); + QCOMPARE(textInput->boundingRect().left(), qreal(0)); canvas.requestActivateWindow(); QTest::qWaitForWindowShown(&canvas); @@ -1400,15 +1399,15 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft() platformInputContext.setInputDirection(Qt::LeftToRight); QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft); - QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0)); + QCOMPARE(textInput->boundingRect().left(), qreal(0)); QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged())); platformInputContext.setInputDirection(Qt::RightToLeft); QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft); QCOMPARE(cursorRectangleSpy.count(), 1); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); // set input direction while having content platformInputContext.setInputDirection(Qt::LeftToRight); @@ -1417,8 +1416,8 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft() QTest::keyClick(&canvas, Qt::Key_Backspace); QVERIFY(textInput->text().isEmpty()); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); // input direction changed while not having focus platformInputContext.setInputDirection(Qt::LeftToRight); @@ -1426,13 +1425,13 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft() platformInputContext.setInputDirection(Qt::RightToLeft); textInput->setFocus(true); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); textInput->setHAlign(QQuickTextInput::AlignRight); QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1); - QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1); + QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1); + QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1); } void tst_qquicktextinput::verticalAlignment() @@ -1442,33 +1441,30 @@ void tst_qquicktextinput::verticalAlignment() QVERIFY(textInput != 0); canvas.show(); - QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput); - QVERIFY(textInputPrivate != 0); - QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop); - QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll < canvas.height() / 2); + QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2); QVERIFY(textInput->cursorRectangle().bottom() < canvas.height() / 2); QVERIFY(textInput->positionToRectangle(0).bottom() < canvas.height() / 2); // bottom aligned textInput->setVAlign(QQuickTextInput::AlignBottom); QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom); - QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll > canvas.height() / 2); + QVERIFY(textInput->boundingRect().top() > canvas.height() / 2); QVERIFY(textInput->cursorRectangle().top() > canvas.height() / 2); QVERIFY(textInput->positionToRectangle(0).top() > canvas.height() / 2); // explicitly center aligned textInput->setVAlign(QQuickTextInput::AlignVCenter); QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter); - QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll < canvas.height() / 2); - QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll > canvas.height() / 2); + QVERIFY(textInput->boundingRect().top() < canvas.height() / 2); + QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2); QVERIFY(textInput->cursorRectangle().top() < canvas.height() / 2); QVERIFY(textInput->cursorRectangle().bottom() > canvas.height() / 2); QVERIFY(textInput->positionToRectangle(0).top() < canvas.height() / 2); QVERIFY(textInput->positionToRectangle(0).bottom() > canvas.height() / 2); } -void tst_qquicktextinput::boundingRect() +void tst_qquicktextinput::clipRect() { QQmlComponent component(&engine); component.setData("import QtQuick 2.0\n TextInput {}", QUrl()); @@ -1476,21 +1472,29 @@ void tst_qquicktextinput::boundingRect() QQuickTextInput *input = qobject_cast(object.data()); QVERIFY(input); - QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width()); - QCOMPARE(input->height(), input->boundingRect().height()); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width()); + QCOMPARE(input->clipRect().height(), input->height()); input->setText("Hello World"); - QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width()); - QCOMPARE(input->height(), input->boundingRect().height()); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width()); + QCOMPARE(input->clipRect().height(), input->height()); - // bounding rect shouldn't exceed the size of the item, expect for the cursor width; + // clip rect shouldn't exceed the size of the item, expect for the cursor width; input->setWidth(input->width() / 2); - QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width()); - QCOMPARE(input->height(), input->boundingRect().height()); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width()); + QCOMPARE(input->clipRect().height(), input->height()); input->setHeight(input->height() * 2); - QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width()); - QCOMPARE(input->height(), input->boundingRect().height()); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width()); + QCOMPARE(input->clipRect().height(), input->height()); QQmlComponent cursorComponent(&engine); cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl()); @@ -1498,8 +1502,122 @@ void tst_qquicktextinput::boundingRect() input->setCursorDelegate(&cursorComponent); // If a cursor delegate is used it's size should determine the excess width. - QCOMPARE(input->width() + 8, input->boundingRect().width()); - QCOMPARE(input->height(), input->boundingRect().height()); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + 8); + QCOMPARE(input->clipRect().height(), input->height()); + + // Alignment, auto scroll, wrapping all don't affect the clip rect. + input->setAutoScroll(false); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + 8); + QCOMPARE(input->clipRect().height(), input->height()); + + input->setHAlign(QQuickTextInput::AlignRight); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + 8); + QCOMPARE(input->clipRect().height(), input->height()); + + input->setWrapMode(QQuickTextInput::Wrap); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + 8); + QCOMPARE(input->clipRect().height(), input->height()); + + input->setVAlign(QQuickTextInput::AlignBottom); + QCOMPARE(input->clipRect().x(), qreal(0)); + QCOMPARE(input->clipRect().y(), qreal(0)); + QCOMPARE(input->clipRect().width(), input->width() + 8); + QCOMPARE(input->clipRect().height(), input->height()); +} + +void tst_qquicktextinput::boundingRect() +{ + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\n TextInput {}", QUrl()); + QScopedPointer object(component.create()); + QQuickTextInput *input = qobject_cast(object.data()); + QVERIFY(input); + + QTextLayout layout; + layout.setFont(input->font()); + + if (!qmlDisableDistanceField()) { + QTextOption option; + option.setUseDesignMetrics(true); + layout.setTextOption(option); + } + layout.beginLayout(); + QTextLine line = layout.createLine(); + layout.endLayout(); + + QCOMPARE(input->boundingRect().x(), qreal(0)); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width()); + QCOMPARE(input->boundingRect().height(), line.height()); + + input->setText("Hello World"); + + layout.setText(input->text()); + layout.beginLayout(); + line = layout.createLine(); + layout.endLayout(); + + QCOMPARE(input->boundingRect().x(), qreal(0)); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width()); + QCOMPARE(input->boundingRect().height(), line.height()); + + // the size of the bounding rect shouldn't be bounded by the size of item. + input->setWidth(input->width() / 2); + QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth()); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width()); + QCOMPARE(input->boundingRect().height(), line.height()); + + input->setHeight(input->height() * 2); + QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth()); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width()); + QCOMPARE(input->boundingRect().height(), line.height()); + + QQmlComponent cursorComponent(&engine); + cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl()); + + input->setCursorDelegate(&cursorComponent); + + // Don't include the size of a cursor delegate as it has its own bounding rect. + QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth()); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth()); + QCOMPARE(input->boundingRect().height(), line.height()); + + // Bounding rect left aligned when auto scroll is disabled; + input->setAutoScroll(false); + QCOMPARE(input->boundingRect().x(), qreal(0)); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth()); + QCOMPARE(input->boundingRect().height(), line.height()); + + input->setHAlign(QQuickTextInput::AlignRight); + QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth()); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth()); + QCOMPARE(input->boundingRect().height(), line.height()); + + input->setWrapMode(QQuickTextInput::Wrap); + QCOMPARE(input->boundingRect().right(), input->width()); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QVERIFY(input->boundingRect().width() < line.naturalTextWidth()); + QVERIFY(input->boundingRect().height() > line.height()); + + input->setVAlign(QQuickTextInput::AlignBottom); + QCOMPARE(input->boundingRect().right(), input->width()); + QCOMPARE(input->boundingRect().bottom(), input->height()); + QVERIFY(input->boundingRect().width() < line.naturalTextWidth()); + QVERIFY(input->boundingRect().height() > line.height()); } void tst_qquicktextinput::positionAt()