Text editing improvements in Qt Quick
August 09, 2024 by Shawn Rutledge | Comments
In the effort to make Qt Quick and Qt Quick Controls a suitable replacement for Widgets in more applications, we identified the text editing use case as something that needed improvement, since many applications need it, and since a text editor is a common sort of demo application that tends to be rewritten in various ways over the years.
Access to the document object
An instance of TextEdit (or its subclass TextArea) uses a QTextDocument as the "model" to be rendered and interactively edited. Early in Qt 5, the textDocument property was added as a way to expose the internally-created QTextDocument instance to your application's C++ code; the first use case was to allow installing a QSyntaxHighlighter.
But users have been asking not only to access the internally-created instance, but to be able to replace it. To make that possible, we had to get rid of the old private QQuickTextDocumentWithImageResources subclass and find another way to handle resource loading (such as inline images loaded over http). QTextDocument::loadResource() looks for a method with the signature QVariant loadResource(int, QUrl) in its parent object; accordingly, QQuickTextEdit::loadResource() is now implemented, and by default, TextEdit is the parent of its own document.
QQuickTextDocument::setTextDocument() is new in Qt 6.7. If you replace the document object, remote resource loading becomes your responsibility, if you need it. It can be done by the parent object's loadResource() function, by a resourceProvider(), or by overriding loadResource() in your own QTextDocument subclass.
Loading and saving
TextEdit.textDocument already existed, so the QQuickTextDocument object that it returns was an obvious place to add new QML-facing API.
The pattern for loading media files so far in QML is to have a source property that takes a URL. We added this property to QQuickTextDocument, so you can set TextEdit.textDocument.source just as you can set Image.source.
Until now, QML has offered almost no API for writing files. (One exception is that Item.grabToImage() returns an object that has a saveToFile() function. It's useful for saving screenshots in your application.) Perhaps in the future we should have a general file I/O API; but for now we don't have it, and nearly every text editor needs to be able to save files, ideally without having to write boilerplate C++ code each time. So we added TextDocument.save() and saveAs() functions for now.
The new API has Tech Preview status, until we resolve whether or not to replace it with a more general-purpose file I/O API. In a widget application, you need to get the raw contents of the QTextDocument from one of its accessors such as toHtml() or toMarkdown() and then write it with QFile; maybe the QML API could work that way too. The pattern of using a source property for loading seems particularly succinct and declarative by comparison; but we are concerned whether the save()/saveAs() API is safe enough and flexible enough for everyone's use cases. Feedback is welcome about this.
One thing that came up right away is if you need to extract data from text that is being loaded or modify what is being saved, for example to deal with YAML metadata in Markdown files, the TextDocument QML API does not directly accommodate that. We are adding QTextDocument::metaInformation(FrontMatter) in 6.8. (Parsing the front matter is orthogonal; for example you could use yaml-cpp. There are also other formats in use besides YAML.)
File conversion is possible too. The existing textFormat property now allows you to convert between formats, and also to toggle between WYSIWYG and raw markup.
Text formatting API
In C++, one often needs a QTextCursor to define a range of text to modify and apply styling to it. The analogous QML type is TextSelection, which (so far) provides alignment, color, font and text properties. TextEdit.cursorSelection corresponds to the text that the user has selected, so you can have bindings to controls to show and change block and character format properties. See the Text Editor example for details.
Large documents
As pointed out on a What's New page a while back: in the last few Qt versions, Text and TextEdit avoid generating scene graph nodes for large portions of text that fall outside the viewport. This is in fact based on a general mechanism for an Item to act as the viewport for (some of) its children, introduced here: the parent (such as Flickable) sets its ItemIsViewport flag, and the child (such as TextEdit) sets its ItemObservesViewport flag. The child's clipRect() provides the region that is visible in the viewport. TextEdit then populates scenegraph nodes only for text blocks that overlap the clipRect, if the text is considered "large" enough to need this optimization. The tradeoff is that its updatePaintNode() is called more often during scrolling, and each time it needs to figure out which blocks should be visible in the viewport.
In the screen recording below, the inner rectangle shows the extents of the viewport, and you can see how paragraphs disappear when they are scrolled outside that region. The Markdown file contains the entire text of a book which is known for its length; and yet the memory usage is more reasonable than it was in older versions of Qt that populated scene graph nodes for all blocks at once:
Going forward
In summary, you can write a WYSIWYG rich text editor in pure QML now. The Text Editor example that we ship no longer needs C++ beyond a basic main() function.
Blog Topics:
Comments
Subscribe to our newsletter
Subscribe Newsletter
Try Qt 6.9 Now!
Download the latest release here: www.qt.io/download.
Qt 6.9 is now available, with new features and improvements for application developers and device creators.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.
Commenting for this post has ended.
Awesome :] Any news about potential changes in QWebSockets module? :P
Hey, thanks! For 6.0 it was decided that we want to have a more limited scope to focus on the core/base components, so QWebSockets is not 'officially' supported in 6.0. But it will be brought back shortly after! I don't know anything more about the plans beyond that I'm afraid.
Could you remove the limit of 6 instances of QNetworkAccessManager? And I can't understand why you do this limit for Qt5?
There's no limit on how many instances of QNetworkAccessManager you can have (although our documentation recommends only having one instance). Are you perhaps talking about the limit of parallel connections QNetworkAccessManager will make in the background to a single host? That limit only counts for HTTP 1(.1) and was/is fairly standard. For HTTP/2 it's actually limited to only 1 connection, but thanks to how the protocol is made up it can actually send multiple requests per connection, so it doesn't need any more.
Yes, I am talking about the parallel connections. But I still hope you to remove this limit because QNetworkAccessManager is a very useful library et everywhere needs it, even in some urgent cases. For example, in the Security Industry with alarms, we hope continuously post the images immediately. If you still won't change ideas, it doesn't matter. But if we buy a commercial license, could you help us to remove this limit?? Thanks.
PS, the Http protocol is the best way to communicate with other languages et system, doesn't it? So I hope you can seriously think about it. Thanks again.
If you compile Qt on your own then as far as I'm aware (never tried) this is the only line that needs to be changed to increase it: https://github.com/qt/qtbase/blob/dev/src/network/access/qhttpnetworkconnection.cpp#L68
Thanks, I will try immediately. But I want to know what's your philosophy to set the value 6 ? I know a book named "the philosophy of Unix" which explains many things at the top level. And I want to get the same philosophy from you.
I wasn't around when the choice was made so I don't know the definitive source, but it was chosen to be 6 since that matched what major browsers used at the time (and largely still do for HTTP 1) and was considered a reasonable fit for Qt as well.
https://stackoverflow.com/a/30064610/2493610
You are so kind and I now know everything about it. In fact, the browsers are the applications for the client, so they can set the value 6 because peoples havn't 20 or 1000 eyes (another reason is the work of rendering is heavy). But Qt is a neutral library, the developers should do what they want, only limited by OS or physical. So removing the limit is still a good idea, this is only my suggestion, you can do as you like.
Last question, if I modify it myself, set defaultHttpChannelCount = 1000 even 60000, that's enough? Should I modify the other things, by example, which are the best values for defaultPipelineLength and defaultRePipelineLength when HttpChannelCount is 1000 or 60000? Sorry I have troubled you so much. But this is really an interesting thing.
Right, as I mentioned earlier I haven't tried it myself, but in theory changing defaultHttpChannelCount is enough. Pipelining isn't enabled by default so unless you enable it it's not going to make a difference what you set those to. Do keep in mind that if you set it to 60000 then it will allocate 60000 channels as well, even if for some connections you only end up using 10. There might also be limits on the server on how many connections you, or perhaps anyone in total, can establish before they start getting dropped.
I recommend to vote and subscribe to and maybe comment on https://bugreports.qt.io/browse/QTBUG-25280 :-)
good to see that there's some work on QTBUG-86966 to provide infos on Network Connection State. really need this for nearly all of my apps.
Hi to all What I can use insteed of QNetworkConfiguration Thank you
QNetworkConfiguration has a few things available. There's no replacement for it in 6.0 though. What about QNC are you currently using?
Can be the linking between Qt Network and QML optional? I don't see why should I deploy Qt Network in an application that does not uses network capabilities at all.
Redirect not happening automatically and I can't seem to get it to work with any policy.
Where is the FTP plugin in Qt 6.1.2?
Still no FTP plugin or support in Qt 6.2?