qt/qml: labels to require opt-in to rich text - #10972
Conversation
b180bd5 to
0c88fea
Compare
1e37407 to
d94ef5c
Compare
d94ef5c to
28db4ce
Compare
|
The Timelock recovery plugin now renders HTML as text. And here's the after: Removing the markup from - "description": "<br/>This plug-in allows you to create Timelock Recovery Plans for your wallet. See: <a href='https://timelockrecovery.com'>timelockrecovery.com</a>",
+ "description": "This plug-in allows you to create Timelock Recovery Plans for your wallet. See: https://timelockrecovery.com", |
Thanks. Fixed in f26429e |
|
I was thinking of a workaround the long tooltips and I think a utility function that wraps the the text before setting the tooltip could do? something like: import textwrap
def wrap_tooltip_text(text: str, *, width: int = 60) -> str:
return "\n".join(
textwrap.fill(line, width=width, break_on_hyphens=False)
for line in text.split("\n")
)and then here: cb.setToolTip(wrap_tooltip_text(long_desc))and the same in Here's a before: and after: |
f26429e to
3067352
Compare
Oh cool! Thanks. I did not know about that module in the standard library :) |
3067352 to
1d5de54
Compare
| "name": "trustedcoin", | ||
| "fullname": "Two Factor Authentication", | ||
| "description": "This plugin adds two-factor authentication to your wallet.<br/>For more information, visit <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>", | ||
| "description": "This plugin adds two-factor authentication to your wallet.\nFor more information, visit https://api.trustedcoin.com/#/electrum-help", |
There was a problem hiding this comment.
we could as well add an explicit "url" section to the manifest.json of plugins.
There was a problem hiding this comment.
Sure, we can do that. Would rather not do it in this PR though.
This comment was marked as outdated.
This comment was marked as outdated.
13c96a1 to
5ad7060
Compare
46e1f4d to
62c32c2
Compare
Security-by-default, instead of convenience: require programmer to opt-in to RichText. This patches Qt only for the Android build, and adds a runtime regression check. We mainly use the QML GUI for Android. However this leaves the Linux desktop dev environment and potential Linux phone users uncovered :/
62c32c2 to
c2a0a03
Compare
You have to opt-in to rich text if you want it for your label!
Upstream chose convenience-by-default for their GUI framework. Shame. :)
-----
earlier, less satisfactory attempt:
```
def eventFilter(self, obj: QObject, event: QEvent) -> bool:
if not isinstance(event, QtCore.QChildEvent):
return False
# - ChildAdded event: already constructed QWidget gets parented
# - note: does *not* fire if parent gets set at construction time, e.g. Label("", parent=self)
# - ChildPolished event:
# - see https://doc.qt.io/qt-6/qstyle.html#polish :
# > This function is called for every widget at some point after it has been fully created
# > but just before it is shown for the very first time.
if event.type() not in (QEvent.Type.ChildAdded, QEvent.Type.ChildPolished):
return False
child = event.child()
if not isinstance(child, QLabel):
return False
if child.textFormat() != Qt.TextFormat.AutoText:
# non-default textFormat => we leave it alone
return False
child.setTextFormat(Qt.TextFormat.PlainText)
return False
```
this special-case QApplication is not covered by InjectNoRichTextEventFilter
- InjectNoRichTextEventFilter is changing AutoText to PlainText, and that also affects the internal QLabel inside the message box
llm output: > Linux with a screen reader running. Here the accessibility bridge queries the label’s text from inside > setText(), and Qt parses the HTML at that moment. This also affects custom_message_box(rich_text=False), > which passes the text to the QMessageBox constructor before switching it to PlainText.
tooltips use QLabel internally, so due to InjectNoRichTextEventFilter, their textFormat is no longer AutoText, but is now PlainText instead. note: messages.to_rtf() was specifically introduced for usage with tooltips [0], I guess to nudge Qt to break longer lines more often (so that tooltips widths are smaller). Tooltips being forced to PlainText breaks that. I also cannot see an easy way to opt-in the tooltip of e.g. a QCheckBox to RichText. Instead, now we use the "textwrap" module from the stdlib to manually word-wrap the strings. [0]: spesmilo@345c2b4 Co-authored-by: Alwoch <salwoch@gmail.com>
- rich text can contain embedded base64-encoded images - qt docs say [0] it is not safe to parse untrusted input as an image [0]: https://doc.qt.io/qt-6/qtimageformats-index.html : > Security Considerations: > Since these file formats are more rarely used, the codecs may be > less thoroughly debugged against potential security holes. As always, > care should be taken when creating applications that may be used > to decode uncontrolled data files.
c2a0a03 to
e258401
Compare
|
Well I think this is as good as I can get it, and ready for merge. |




I want ~all text in the GUIs to be plaintext by default. Every text field that expects/wants to render rich text must explicitly opt-in to that behaviour.
QLabel in QtWidgets, and Label in QML, default their textFormat to AutoText, which means they allow and render rich text as needed.
It is not just labels though: QML also has at least TextArea and TextEdit, and QtWidgets has QMessageBox.
The designers of Qt chose convenience-by-default, but I want security (or rather, sanity)-by-default.
(I don't know whether this PR is the best way to implement this though, and I don't mind if we come up with something else instead, all I care about is the end-result described in the first paragraph.)
Note: there is a proposal on the Qt mailing list to change the default to PlainText, in Qt7, which has some traction (?)