Add QQuickText::hoveredLink
authorJ-P Nurmi <jpnurmi@digia.com>
Tue, 23 Apr 2013 13:29:56 +0000 (15:29 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Tue, 18 Jun 2013 05:50:11 +0000 (07:50 +0200)
Task-number: QTBUG-30804
Change-Id: I6c6993b152285f4bdf34d6e1aa04f25fa7ca41e0
Reviewed-by: Alan Alpert <aalpert@blackberry.com>
src/quick/items/qquickitemsmodule.cpp
src/quick/items/qquicktext.cpp
src/quick/items/qquicktext_p.h
src/quick/items/qquicktext_p_p.h
tests/auto/quick/qquicktext/tst_qquicktext.cpp

index 9b3f6ee..6f1edc7 100644 (file)
@@ -236,6 +236,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
     qmlRegisterType<QQuickGridView, 1>(uri, 2, 1, "GridView");
     qmlRegisterType<QQuickTextEdit, 1>(uri, 2, 1, "TextEdit");
 
+    qmlRegisterType<QQuickText, 2>(uri, 2, 2, "Text");
     qmlRegisterType<QQuickTextEdit, 2>(uri, 2, 2, "TextEdit");
 }
 
index b46f2e5..cc2cbb3 100644 (file)
@@ -107,6 +107,7 @@ void QQuickTextPrivate::init()
     Q_Q(QQuickText);
     q->setAcceptedMouseButtons(Qt::LeftButton);
     q->setFlag(QQuickItem::ItemHasContents);
+    q->setAcceptHoverEvents(true);
 }
 
 QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent)
@@ -2581,4 +2582,83 @@ void QQuickText::mouseReleaseEvent(QMouseEvent *event)
         QQuickItem::mouseReleaseEvent(event);
 }
 
+bool QQuickTextPrivate::isLinkHoveredConnected()
+{
+    Q_Q(QQuickText);
+    IS_SIGNAL_CONNECTED(q, QQuickText, linkHovered, (const QString &));
+}
+
+/*!
+    \qmlsignal QtQuick2::Text::onLinkHovered(string link)
+    \since QtQuick 2.2
+
+    This handler is called when the user hovers a link embedded in the
+    text. The link must be in rich text or HTML format and the \a link
+    string provides access to the particular link.
+
+    \sa hoveredLink
+*/
+
+/*!
+    \qmlproperty string QtQuick2::Text::hoveredLink
+    \since QtQuick 2.2
+
+    This property contains the link string when user hovers a link
+    embedded in the text. The link must be in rich text or HTML format
+    and the \a hoveredLink string provides access to the particular link.
+
+    \sa onLinkHovered
+*/
+
+QString QQuickText::hoveredLink() const
+{
+    Q_D(const QQuickText);
+    if (const_cast<QQuickTextPrivate *>(d)->isLinkHoveredConnected()) {
+        if (d->extra.isAllocated())
+            return d->extra->hoveredLink;
+    } else {
+#ifndef QT_NO_CURSOR
+        if (QQuickWindow *wnd = window()) {
+            QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
+            return d->anchorAt(pos);
+        }
+#endif // QT_NO_CURSOR
+    }
+    return QString();
+}
+
+void QQuickTextPrivate::processHoverEvent(QHoverEvent *event)
+{
+    Q_Q(QQuickText);
+    QString link;
+    if (event->type() != QEvent::HoverLeave)
+        link = anchorAt(event->posF());
+
+    if ((!extra.isAllocated() && !link.isEmpty()) || (extra.isAllocated() && extra->hoveredLink != link)) {
+        extra.value().hoveredLink = link;
+        emit q->linkHovered(extra->hoveredLink);
+    }
+}
+
+void QQuickText::hoverEnterEvent(QHoverEvent *event)
+{
+    Q_D(QQuickText);
+    if (d->isLinkHoveredConnected())
+        d->processHoverEvent(event);
+}
+
+void QQuickText::hoverMoveEvent(QHoverEvent *event)
+{
+    Q_D(QQuickText);
+    if (d->isLinkHoveredConnected())
+        d->processHoverEvent(event);
+}
+
+void QQuickText::hoverLeaveEvent(QHoverEvent *event)
+{
+    Q_D(QQuickText);
+    if (d->isLinkHoveredConnected())
+        d->processHoverEvent(event);
+}
+
 QT_END_NAMESPACE
index 03b436b..f34cf17 100644 (file)
@@ -90,6 +90,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
     Q_PROPERTY(int minimumPointSize READ minimumPointSize WRITE setMinimumPointSize NOTIFY minimumPointSizeChanged)
     Q_PROPERTY(FontSizeMode fontSizeMode READ fontSizeMode WRITE setFontSizeMode NOTIFY fontSizeModeChanged)
     Q_PROPERTY(RenderType renderType READ renderType WRITE setRenderType NOTIFY renderTypeChanged)
+    Q_PROPERTY(QString hoveredLink READ hoveredLink NOTIFY linkHovered REVISION 2)
 
 public:
     QQuickText(QQuickItem *parent=0);
@@ -207,9 +208,12 @@ public:
     RenderType renderType() const;
     void setRenderType(RenderType renderType);
 
+    QString hoveredLink() const;
+
 Q_SIGNALS:
     void textChanged(const QString &text);
     void linkActivated(const QString &link);
+    Q_REVISION(2) void linkHovered(const QString &link);
     void fontChanged(const QFont &font);
     void colorChanged();
     void linkColorChanged();
@@ -243,6 +247,10 @@ protected:
 
     void updatePolish();
 
+    void hoverEnterEvent(QHoverEvent *event);
+    void hoverMoveEvent(QHoverEvent *event);
+    void hoverLeaveEvent(QHoverEvent *event);
+
 private Q_SLOTS:
     void q_imagesLoaded();
     void triggerPreprocess();
index ff6b0f2..7a31e77 100644 (file)
@@ -87,6 +87,8 @@ public:
     QString elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine = 0) const;
     void elideFormats(int start, int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats);
 
+    void processHoverEvent(QHoverEvent *event);
+
     QRectF layedOutTextRect;
 
     struct ExtraData {
@@ -95,6 +97,7 @@ public:
         qreal lineHeight;
         QQuickTextDocumentWithImageResources *doc;
         QString activeLink;
+        QString hoveredLink;
         int minimumPixelSize;
         int minimumPointSize;
         int nbActiveDownloads;
@@ -167,6 +170,7 @@ public:
     QRectF setupTextLayout(qreal * const baseline);
     void setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset = 0);
     bool isLinkActivatedConnected();
+    bool isLinkHoveredConnected();
     static QString anchorAt(const QTextLayout *layout, const QPointF &mousePos);
     QString anchorAt(const QPointF &pos) const;
 
index fb3b62b..fdaa1d6 100644 (file)
@@ -110,8 +110,8 @@ private slots:
     void letterSpacing();
     void wordSpacing();
 
-    void clickLink_data();
-    void clickLink();
+    void linkInteraction_data();
+    void linkInteraction();
 
     void implicitSize_data();
     void implicitSize();
@@ -1482,15 +1482,30 @@ void tst_qquicktext::wordSpacing()
 class EventSender : public QQuickItem
 {
 public:
-    void sendEvent(QMouseEvent *event) {
-        if (event->type() == QEvent::MouseButtonPress)
-            mousePressEvent(event);
-        else if (event->type() == QEvent::MouseButtonRelease)
-            mouseReleaseEvent(event);
-        else if (event->type() == QEvent::MouseMove)
-            mouseMoveEvent(event);
-        else
+    void sendEvent(QEvent *event) {
+        switch (event->type()) {
+        case QEvent::MouseButtonPress:
+            mousePressEvent(static_cast<QMouseEvent *>(event));
+            break;
+        case QEvent::MouseButtonRelease:
+            mouseReleaseEvent(static_cast<QMouseEvent *>(event));
+            break;
+        case QEvent::MouseMove:
+            mouseMoveEvent(static_cast<QMouseEvent *>(event));
+            break;
+        case QEvent::HoverEnter:
+            hoverEnterEvent(static_cast<QHoverEvent *>(event));
+            break;
+        case QEvent::HoverLeave:
+            hoverLeaveEvent(static_cast<QHoverEvent *>(event));
+            break;
+        case QEvent::HoverMove:
+            hoverMoveEvent(static_cast<QHoverEvent *>(event));
+            break;
+        default:
             qWarning() << "Trying to send unsupported event type";
+            break;
+        }
     }
 };
 
@@ -1500,10 +1515,12 @@ class LinkTest : public QObject
 public:
     LinkTest() {}
 
-    QString link;
+    QString clickedLink;
+    QString hoveredLink;
 
 public slots:
-    void linkClicked(QString l) { link = l; }
+    void linkClicked(QString l) { clickedLink = l; }
+    void linkHovered(QString l) { hoveredLink = l; }
 };
 
 class TextMetrics
@@ -1589,13 +1606,15 @@ public:
 typedef QVector<QPointF> PointVector;
 Q_DECLARE_METATYPE(PointVector);
 
-void tst_qquicktext::clickLink_data()
+void tst_qquicktext::linkInteraction_data()
 {
     QTest::addColumn<QString>("text");
     QTest::addColumn<qreal>("width");
     QTest::addColumn<QString>("bindings");
     QTest::addColumn<PointVector>("mousePositions");
-    QTest::addColumn<QString>("link");
+    QTest::addColumn<QString>("clickedLink");
+    QTest::addColumn<QString>("hoverEnterLink");
+    QTest::addColumn<QString>("hoverMoveLink");
 
     const QString singleLineText = "this text has a <a href=\\\"http://qt-project.org/single\\\">link</a> in it";
     const QString singleLineLink = "http://qt-project.org/single";
@@ -1612,196 +1631,229 @@ void tst_qquicktext::clickLink_data()
                 << singleLineText << 240.
                 << ""
                 << (PointVector() << metrics.characterRectangle(18).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
         QTest::newRow("click on text")
                 << singleLineText << 240.
                 << ""
                 << (PointVector() << metrics.characterRectangle(13).center())
-                << QString();
+                << QString()
+                << QString() << QString();
         QTest::newRow("drag within link")
                 << singleLineText << 240.
                 << ""
                 << (PointVector()
                     << metrics.characterRectangle(17).center()
                     << metrics.characterRectangle(19).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
         QTest::newRow("drag away from link")
                 << singleLineText << 240.
                 << ""
                 << (PointVector()
                     << metrics.characterRectangle(18).center()
                     << metrics.characterRectangle(13).center())
-                << QString();
+                << QString()
+                << singleLineLink << QString();
         QTest::newRow("drag on to link")
                 << singleLineText << 240.
                 << ""
                 << (PointVector()
                     << metrics.characterRectangle(13).center()
                     << metrics.characterRectangle(18).center())
-                << QString();
+                << QString()
+                << QString() << singleLineLink;
         QTest::newRow("click on bottom right aligned link")
                 << singleLineText << 240.
                 << "horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom"
                 << (PointVector() << metrics.characterRectangle(18, Qt::AlignRight, Qt::AlignBottom).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
         QTest::newRow("click on center aligned link")
                 << singleLineText << 240.
                 << "horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter"
                 << (PointVector() << metrics.characterRectangle(18, Qt::AlignHCenter, Qt::AlignVCenter).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
         QTest::newRow("click on rich text link")
                 << singleLineText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector() << metrics.characterRectangle(18).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
         QTest::newRow("click on rich text")
                 << singleLineText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector() << metrics.characterRectangle(13).center())
-                << QString();
+                << QString()
+                << QString() << QString();
         QTest::newRow("click on bottom right aligned rich text link")
                 << singleLineText << 240.
                 << "textFormat: Text.RichText; horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom"
                 << (PointVector() << metrics.characterRectangle(18, Qt::AlignRight, Qt::AlignBottom).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
         QTest::newRow("click on center aligned rich text link")
                 << singleLineText << 240.
                 << "textFormat: Text.RichText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter"
                 << (PointVector() << metrics.characterRectangle(18, Qt::AlignHCenter, Qt::AlignVCenter).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
     } {
         const TextMetrics metrics("this text has a li", Qt::ElideRight);
         QTest::newRow("click on right elided link")
                 << singleLineText << metrics.width() +  2
                 << "elide: Text.ElideRight"
                 << (PointVector() << metrics.characterRectangle(17).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
     } {
         const TextMetrics metrics("ink in it", Qt::ElideLeft);
         QTest::newRow("click on left elided link")
                 << singleLineText << metrics.width() +  2
                 << "elide: Text.ElideLeft"
                 << (PointVector() << metrics.characterRectangle(2).center())
-                << singleLineLink;
+                << singleLineLink
+                << singleLineLink << singleLineLink;
     } {
         const TextMetrics metrics("this text\nhas multiple\nlines in it");
         QTest::newRow("click on second line")
                 << multipleLineText << 240.
                 << ""
                 << (PointVector() << metrics.characterRectangle(18).center())
-                << multipleLineLink;
+                << multipleLineLink
+                << multipleLineLink << multipleLineLink;
         QTest::newRow("click on third line")
                 << multipleLineText << 240.
                 << ""
                 << (PointVector() << metrics.characterRectangle(25).center())
-                << multipleLineLink;
+                << multipleLineLink
+                << multipleLineLink << multipleLineLink;
         QTest::newRow("drag from second line to third")
                 << multipleLineText << 240.
                 << ""
                 << (PointVector()
                     << metrics.characterRectangle(18).center()
                     << metrics.characterRectangle(25).center())
-                << multipleLineLink;
+                << multipleLineLink
+                << multipleLineLink << multipleLineLink;
         QTest::newRow("click on rich text second line")
                 << multipleLineText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector() << metrics.characterRectangle(18).center())
-                << multipleLineLink;
+                << multipleLineLink
+                << multipleLineLink << multipleLineLink;
         QTest::newRow("click on rich text third line")
                 << multipleLineText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector() << metrics.characterRectangle(25).center())
-                << multipleLineLink;
+                << multipleLineLink
+                << multipleLineLink << multipleLineLink;
         QTest::newRow("drag rich text from second line to third")
                 << multipleLineText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector()
                     << metrics.characterRectangle(18).center()
                     << metrics.characterRectangle(25).center())
-                << multipleLineLink;
+                << multipleLineLink
+                << multipleLineLink << multipleLineLink;
     } {
         const TextMetrics metrics("this text has a nested link in it");
         QTest::newRow("click on left outer link")
                 << nestedText << 240.
                 << ""
                 << (PointVector() << metrics.characterRectangle(22).center())
-                << outerLink;
+                << outerLink
+                << outerLink << outerLink;
         QTest::newRow("click on right outer link")
                 << nestedText << 240.
                 << ""
                 << (PointVector() << metrics.characterRectangle(27).center())
-                << outerLink;
+                << outerLink
+                << outerLink << outerLink;
         QTest::newRow("click on inner link left")
                 << nestedText << 240.
                 << ""
                 << (PointVector() << metrics.characterRectangle(23).center())
-                << innerLink;
+                << innerLink
+                << innerLink << innerLink;
         QTest::newRow("click on inner link right")
                 << nestedText << 240.
                 << ""
                 << (PointVector() << metrics.characterRectangle(26).center())
-                << innerLink;
+                << innerLink
+                << innerLink << innerLink;
         QTest::newRow("drag from inner to outer link")
                 << nestedText << 240.
                 << ""
                 << (PointVector()
                     << metrics.characterRectangle(25).center()
                     << metrics.characterRectangle(30).center())
-                << QString();
+                << QString()
+                << innerLink << outerLink;
         QTest::newRow("drag from outer to inner link")
                 << nestedText << 240.
                 << ""
                 << (PointVector()
                     << metrics.characterRectangle(30).center()
                     << metrics.characterRectangle(25).center())
-                << QString();
+                << QString()
+                << outerLink << innerLink;
         QTest::newRow("click on left outer rich text link")
                 << nestedText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector() << metrics.characterRectangle(22).center())
-                << outerLink;
+                << outerLink
+                << outerLink << outerLink;
         QTest::newRow("click on right outer rich text link")
                 << nestedText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector() << metrics.characterRectangle(27).center())
-                << outerLink;
+                << outerLink
+                << outerLink << outerLink;
         QTest::newRow("click on inner rich text link left")
                 << nestedText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector() << metrics.characterRectangle(23).center())
-                << innerLink;
+                << innerLink
+                << innerLink << innerLink;
         QTest::newRow("click on inner rich text link right")
                 << nestedText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector() << metrics.characterRectangle(26).center())
-                << innerLink;
+                << innerLink
+                << innerLink << innerLink;
         QTest::newRow("drag from inner to outer rich text link")
                 << nestedText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector()
                     << metrics.characterRectangle(25).center()
                     << metrics.characterRectangle(30).center())
-                << QString();
+                << QString()
+                << innerLink << outerLink;
         QTest::newRow("drag from outer to inner rich text link")
                 << nestedText << 240.
                 << "textFormat: Text.RichText"
                 << (PointVector()
                     << metrics.characterRectangle(30).center()
                     << metrics.characterRectangle(25).center())
-                << QString();
+                << QString()
+                << outerLink << innerLink;
     }
 }
 
-void tst_qquicktext::clickLink()
+void tst_qquicktext::linkInteraction()
 {
     QFETCH(QString, text);
     QFETCH(qreal, width);
     QFETCH(QString, bindings);
     QFETCH(PointVector, mousePositions);
-    QFETCH(QString, link);
+    QFETCH(QString, clickedLink);
+    QFETCH(QString, hoverEnterLink);
+    QFETCH(QString, hoverMoveLink);
 
     QString componentStr =
-            "import QtQuick 2.0\nText {\n"
+            "import QtQuick 2.2\nText {\n"
                 "width: " + QString::number(width) + "\n"
                 "height: 320\n"
                 "text: \"" + text + "\"\n"
@@ -1815,28 +1867,46 @@ void tst_qquicktext::clickLink()
 
     LinkTest test;
     QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
+    QObject::connect(textObject, SIGNAL(linkHovered(QString)), &test, SLOT(linkHovered(QString)));
 
     QVERIFY(mousePositions.count() > 0);
 
     QPointF mousePosition = mousePositions.first();
     {
+        QHoverEvent he(QEvent::HoverEnter, mousePosition, QPointF());
+        static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&he);
+
         QMouseEvent me(QEvent::MouseButtonPress, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
         static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
     }
 
+    QCOMPARE(test.hoveredLink, hoverEnterLink);
+    QCOMPARE(textObject->hoveredLink(), hoverEnterLink);
+
     for (int i = 1; i < mousePositions.count(); ++i) {
         mousePosition = mousePositions.at(i);
 
+        QHoverEvent he(QEvent::HoverMove, mousePosition, QPointF());
+        static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&he);
+
         QMouseEvent me(QEvent::MouseMove, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
         static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
     }
 
+    QCOMPARE(test.hoveredLink, hoverMoveLink);
+    QCOMPARE(textObject->hoveredLink(), hoverMoveLink);
+
     {
+        QHoverEvent he(QEvent::HoverLeave, mousePosition, QPointF());
+        static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&he);
+
         QMouseEvent me(QEvent::MouseButtonRelease, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
         static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
     }
 
-    QCOMPARE(test.link, link);
+    QCOMPARE(test.clickedLink, clickedLink);
+    QCOMPARE(test.hoveredLink, QString());
+    QCOMPARE(textObject->hoveredLink(), QString());
 
     delete textObject;
 }