Disabling a QLabel with links

Submitted by mimec on 2013-09-16

Qt doesn't have an equivalent of .NET's LinkLabel control, because the regular QLabel supports HTML content, including links. It's easy to create a link label using an anchor tag (i.e. <a href="...">...</a>). You can then handle the linkActivated signal or simply set the openExternalLinks property to true if you want the label to automatically open the link target in a web browser. However, there is one small limitation of the standard label widget: when you disable it, the links no longer work, but they are still drawn the same way as regular links (i.e. as blue underlined text).

Recently I had to slighty modify the notification schedule editor in WebIssues which looks like this:

Enabled labels

The links on the right make it easier to check or uncheck all options at once. However, the entire schedule needs to be disabled if sending summary emails is turned off:

Disabled labels

Note that the links are drawn in the same way as the regular disabled label and the checkboxes. How to achieve such effect?

After some research it turned out that tweaking the label's palette and/or style sheet doesn't do the job as expected. The workaround is to include the appropriate style directly in the anchor tag, for example:

QColor color = palette().color( QPalette::Disabled, QPalette::WindowText );
label->setText( QString( "<a href=\"%1\" style=\"color: %2; text-decoration: none;\">%3</a>" )
    .arg( linkUrl, color.name(), linkCaption );

Note that the disabled text color from the current palette is passed to the style definition so that it looks the same as the regular disabled label.

To make it easier, I wrote a very simple subclass of QLabel which automatically generates the correct HTML when it becomes enabled or disabled:

class LinkLabel : public QLabel
{
    Q_OBJECT
public:
    LinkLabel( const QString& caption, QWidget* parent ) : QLabel( parent ),
        m_linkUrl( "#" ),
        m_linkCaption( caption )
    {
        setTextFormat( Qt::RichText );
        updateLink();
    }

    ~LinkLabel() { }

public:
    void setLinkUrl( const QString& url )
    {
        m_linkUrl = url;
        updateLink();
    }

    const QString& linkUrl() const { return m_linkUrl; }

    void setLinkCaption( const QString& caption )
    {
        m_linkCaption = caption;
        updateLink();
    }

    const QString& linkCaption() const { return m_linkCaption; }

protected: // overrides
    void changeEvent( QEvent* e )
    {
        QLabel::changeEvent( e );
        if ( e->type() == QEvent::EnabledChange || e->type() == QEvent::PaletteChange )
            updateLink();
    }

private:
    void updateLink()
    {
        QString style;
        if ( !isEnabled() ) {
            QColor color = palette().color( QPalette::Disabled, QPalette::WindowText );
            style = QString( " style=\"color: %1; text-decoration: none;\"" ).arg( color.name() );
        }
        setText( QString( "<a href=\"%1\"%2>%3</a>" ).arg( Qt::escape( m_linkUrl ), style,
            Qt::escape( m_linkCaption ) ) );
    }

private:
    QString m_linkUrl;
    QString m_linkCaption;
};

Note that in this example the link's URL defaults to "#", which is convenient if the link is handled internally using the linkActivated signal. Of course you can overload the contstructor so that it takes the URL as a parameter. Also it's not very difficult to modify the code so that a single label could contain multiple links.