npht
npht

Reputation: 35

How to set tooltips in qml text

Is there some way to show a tip when I hover some word in my qml text? For examle I want to see a definiton of the word I hovered in a text.

*Wikipedia website has this feature.

Upvotes: 1

Views: 2150

Answers (3)

ecloud
ecloud

Reputation: 356

Mitch's answer depends on the hover event being free to propagate to the HoverHandler, and we've been having second thoughts about https://codereview.qt-project.org/c/qt/qtdeclarative/+/420965/ But I think since the accepted flag exists, it's nice to be able to use it. And since https://codereview.qt-project.org/c/qt/qtdeclarative/+/400979 we have ruled out some cases, such as having an Item with a HoverHandler that is under or over the item of interest (the Text or Label) without being a child or a parent of it. We might have to change our minds about that if we find too many cases that are too hard to achieve; time will tell.

Since we say that QML is a declarative languge, I'm always looking for ways to write it more declaratively, with less JavaScript. So here's a more-declarative version of Mitch's answer, which also avoids the propagation problem:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
    title: label.hoveredLink.length > 0 ? "hovered link" : hoverHandler.hovered ? "hovered text" : "not hovered"
    width: 640
    height: 480
    visible: true


    Label {
        id: label
        anchors.fill: parent
        text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud <a href=\"Definition goes here\">exercitation</a> ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu <a href=\"Definition goes here\">fugiat</a> nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        wrapMode: Label.Wrap

        onHoveredLinkChanged: (link) => {
            if (link.length > 0) {
                toolTip.x = hoverHandler.point.position.x + 8
                toolTip.y = hoverHandler.point.position.y + 8
            }
        }

        HoverHandler { id: hoverHandler }
    }

    ToolTip {
        id: toolTip
        visible: label.hoveredLink.length > 0
        text: label.hoveredLink
    }
}

The reason that there's still a JS snippet in onHoveredLinkChanged is to fix the tooltip position at the time that you hover. If you don't mind it following the cursor while the link is hovered, you can write it fully declaratively, and as a self-contained component:

import QtQuick
import QtQuick.Controls

Text {
    id: textItem
    width: 480; height: 240
    text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud <a href=\"Definition goes here\">exercitation</a> ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu <a href=\"Definition goes here\">fugiat</a> nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    wrapMode: Text.WordWrap

    HoverHandler { id: hoverHandler }
    ToolTip {
        id: toolTip
        visible: text.length > 0
        text: textItem.hoveredLink
        x: hoverHandler.point.position.x + 8
        y: hoverHandler.point.position.y + 8
    }
}

I think we are missing some functionality that would make it nicer to find hovered words. QtQuick's Text doesn't even have a function to hit-test and find the text cursor position for a pixel position (although we do have linkAt(x, y)); that would be a good start. I have wanted something like that for language learning for years: be able to look up any word that I hover in some sort of dictionary; so that would be nice to have.

Upvotes: 1

user16776498
user16776498

Reputation:

I understood that the OP asked to hover a certain word inside a Text. The following is an alternative to Mitch's answer that supports hovering not only links. With some overhead though.

You can use Grid, Repeater and ListModel. Example:

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    anchors.fill: parent;
    Grid {
        spacing: 10
        anchors.centerIn: parent;
        Repeater {
            anchors.fill: parent;
            model: ListModel {
                ListElement {
                    text: "lorem"
                    tip: ""
                }
                ListElement {
                    text: "ipsum"
                    tip: ""
                }
                ListElement {
                    text: "I have a tip"
                    tip: "Never pee to the pool you drink from"
                    color: "red"

                }
                ListElement {
                    text: "dolor"
                    tip: ""
                }
                ListElement {
                    text: "amet"
                    tip: ""
                }
            }

            delegate:
            Text {
                text: model.text
                color: model.color
                MouseArea {
                    id: _mouseArea
                    hoverEnabled: true
                    anchors.fill: parent
                }
                ToolTip.visible: _mouseArea.containsMouse && model.tip;
                ToolTip.text: model.tip
            }
        }
    }
}

enter image description here

You can easily create C++ / Python model that will fill these roles based on a plain string.


If you want to create your own custom tooltip check this SO and this Github repository or this Github repository.

Upvotes: 3

Mitch
Mitch

Reputation: 24416

To allow hovering over individual words, you can use this code:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
    title: qsTr("Hello World")
    width: 640
    height: 480
    visible: true

    HoverHandler {
        id: hoverHandler
        onHoveredChanged: {
            if (hovered)
                toolTip.hide()
        }
    }

    Label {
        id: label
        anchors.fill: parent
        text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud <a href=\"Definition goes here\">exercitation</a> ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu <a href=\"Definition goes here\">fugiat</a> nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
        wrapMode: Label.Wrap

        onLinkHovered: (link) => {
            if (link.length === 0)
                return

            // Show the ToolTip at the mouse cursor, plus some margins so the mouse doesn't get in the way.
            toolTip.x = hoverHandler.point.position.x + 8
            toolTip.y = hoverHandler.point.position.y + 8
            toolTip.show(link)
        }
    }

    ToolTip {
        id: toolTip
    }
}

It uses Text's linkHovered signal to respond to links being hovered, and HoverHandler to ensure that the ToolTip is hidden when a link isn't hovered. You could also pass a timeout to show() to hide the ToolTip after a certain amount of time, but that's less user-friendly.

screen recording of tooltip

The style used in the GIF is the Material style.

Upvotes: 6

Related Questions