Thursday, 5 July 2018

QLabel swallows mouse clicks in RichText mode?

As I already said in the title:
QLabel swallows mouse clicks when in rich text mode, what can I do? Help! 
I ran into that problem in my new project just yesterday* and will document the solution here, because searching the Internet didn't result in a thorough solution 😞.
Actually, I ran into two problems with Qt (I'm on the 5.9 version now). It started with my desire to change line spacing in the custom icon widget I inherited with a pile of legacy code in my new project. Somehow it wasn't possible with the standard C++ API, so I resorted to the proven Qt workaround - CSS stylesheets.

Oftentimes stylesheet support implements manipulations and parametrizations not available through C++ and this case is one of them. Because setting the stylesheet directly didn't seem to work, the equivalent rich text string would do the work (note that the line-height property is stated in percent values):
  QString txt = "<p style=\"line-height:80;\">text text text</p>";
That trick seemed to have removed the problem, but in reality I just didn't notice the consecutive problem I created with this!

The text I wanted to space was a subscript under an icon, which was set as a bitmap in the class containing both the icon and the text. What I didn't notice was the fact, that a touch/click on the icon would start processing but when the user clicked/touched the re-spaced text it wouldn't! And of course falling back to a non-rich text would fix that problem!

As it turned out, in rich text mode QLabel wouldn't receive any MouseRelease events! First of all I found a slew of Qt bug-reports: 12982, 2028, 24375, all of them rejected or simply not fixed. So is it a Qt bug? Why it's not fixed? And what is a workaround for it? So I started to search the web for hints. One info I found was a short explanation by Thiago Macieira:
"Probably not a bug. Do you get the mousePressEvent? The release event is always sent to the same widget that accepted the mouse release. And QLabel with rich text might contain links, so it may need to handle mouse presses."
Needled to say I was rather devastated - this seems to be the expected behavior, and if you want to change it, you have to reimplement it!!! Needless to say, I wasn't inclined to do so. I was slowly approaching desperation, but then I found another hint"you could try ... Qt::WA_TransparentForMouseEvents". And that put every piece of the puzzle in its place for me:

1. As Qt-documentation says on Qt::WA_TransparentForMouseEvents:
"When enabled, this attribute disables the delivery of mouse events to the widget and its children. Mouse events are delivered to other widgets as if the widget and its children were not present in the widget hierarchy; mouse clicks and other events effectively "pass through" them. This attribute is disabled by default."
2. QLabel might contain links in general, but in out case we just have some plain text which has to be styled, ergo mouse events are of no use to it!

3. We cannot force QLabel to not accept() the mouse events in that case (alas), but we can cut it off from them, so that it won't steal MouseRelease and its parent widget will still receive it.

So I wrote a following method for setting the labels of icons:
  void CIconCell::SetIconLabel(const QString& label)
    // correct spacing in double row texts
    // - cannot be done with std. Qt-API!
    m_Text->setAttribute(Qt::WA_TransparentForMouseEvents, true); // otherwise rich-text QLabel blocks mouse events!

    auto richTxt = QString("<p style=\"line-height:%1;\">").arg(m_LineSpacingRatio);
    richTxt += label;
    richTxt.replace("\n", "<br />");
    richTxt += "</p>";

PS: somehow the code formatter seems to interpret <br /> as line break, it should be: richTxt.replace("\n", "<br />");

So essentially all the information was out there, you only had to connect the dots. But this isn't cost free, so I publish the ready solution for the good of all Qt programmers 😉.

* OK, not entirely true, but at least I started to write this post on the next day...

No comments: