Qt Quick中的文本编辑改进

Text editing improvements in Qt Quick

Qt Quick中的文本编辑改进

August 09, 2024 by Shawn Rutledge | Comments

​2024年8月9日:肖恩·拉特利奇

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.

为了使Qt Quick和Qt Quick Controls成为更多应用程序中Widget的合适替代品,我们确定文本编辑用例需要改进,因为许多应用程序都需要它,而且文本编辑器是一种常见的演示应用程序,多年来往往会以各种方式重写。

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.

​TextEdit的一个实例(或其子类TextArea)使用QTextDocument作为要呈现和交互式编辑的“模型”。在Qt 5的早期,添加了textDocument属性,作为将内部创建的QTextDocument实例暴露给应用程序的C++代码的一种方式;第一个用例是允许安装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.

但用户不仅要求访问内部创建的实例,还要求能够替换它。为了实现这一点,我们必须摆脱旧的私有QQuickTextDocumentWithImageResources子类,并找到另一种处理资源加载的方法(例如通过http加载的内联图像)。QTextDocument::loadResource()在其父对象中查找签名为QVariat loadResource(int,QUrl)的方法;因此,现在实现了QQuickTextEdit::loadResource(),默认情况下,TextEdit是其自己文档的父级。

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.

​QQuickTextDocument::setTextDocument()是Qt 6.7中的新功能。如果你替换了document对象,远程资源加载将成为你的责任,如果需要的话。这可以通过父对象的loadResource()函数、resourceProvider()或在自己的QTextDocument子类中重写loadResource。

Loading and saving

加载与保存

TextEdit.textDocument already existed, so the QQuickTextDocument object that it returns was an obvious place to add new QML-facing API.

TextEdit.textDocument已经存在,因此它返回的QQuickTextDocument对象显然是添加新的面向QML的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.

​到目前为止,QML中加载媒体文件的模式是具有一个接受URL的源属性。我们将此属性添加到QQuickTextDocument中,因此可以设置TextEdit.textDocument.source,就像设置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.

​到目前为止,QML几乎没有提供用于编写文件的API。(一个例外是Item.grabToImage()返回一个具有saveToFile()函数的对象。这对于在应用程序中保存屏幕截图很有用。)也许将来我们应该有一个通用的文件I/O API;但目前我们还没有,几乎每个文本编辑器都需要能够保存文件,理想情况下不必每次都编写样板C++代码。因此,我们现在添加了TextDocument.save()和saveAs()函数。

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.

新的API具有技术预览状态,直到我们决定是否将其替换为更通用的文件I/O API。在widget应用程序中,需要从QTextDocument的一个访问器(如toHtml()或toMarkdown())获取QTextDocument原始内容,然后用QFile编写;也许QML API也可以这样工作。相比之下,使用源属性进行加载的模式似乎特别简洁和声明性;但我们关心的是,save()/saveAs()API对于每个人的用例是否足够安全和灵活。欢迎对此提供反馈。

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.)

​马上出现的一件事是,如果需要从正在加载的文本中提取数据或修改正在保存的内容,例如处理Markdown文件中的YAML元数据,那么TextDocument QML API不会直接适应这一点。我们在6.8中添加了QTextDocument::metaInformation(FrontMatter)。(解析前体是正交的;例如,可以使用yaml-cpp。除了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.

​文件转换也是可能的。现有的textFormat属性现在允许在格式之间进行转换,也可以在所见即所得和原始标记之间切换。

Text formatting API

文本格式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.

​在C++中,人们通常需要一个QTextCursor来定义一系列文本以对其进行修改和应用样式。类似的QML类型是TextSelection,它(到目前为止)提供对齐、颜色、字体和文本属性。TextEdit.cursorSelect对应于用户选择的文本,因此可以绑定到控件以显示和更改块和字符格式属性。有关详细信息,请参见文本编辑器示例。

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.

​正如不久前在What's New页面上指出的那样:在最近几个Qt版本中,Text和TextEdit避免为落在视口之外的大部分文本生成场景图节点。这实际上是基于一种通用机制,即项目充当其(某些)子项目的视口,这里介绍:父项(如Flickable)设置其ItemIsViewport标志,子项(如TextEdit)设置其Items ObservesViewport标志。孩子的clipRect()提供了视口中可见的区域。如果文本被认为“大”到需要这种优化,TextEdit只会为与clipRect重叠的文本块填充场景图节点。权衡的是,它的updatePaintNode()在滚动过程中被更频繁地调用,每次都需要弄清楚哪些块应该在视口中可见。

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:

在下面的屏幕录制中,内部矩形显示了视口的范围,可以看到当段落滚动到该区域之外时,它们是如何消失的。Markdown文件包含一本以长度而闻名的书的全文;然而,与一次性填充所有块的场景图节点的旧版本Qt相比,内存使用更合理:

large-textedit-scrolling

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.

​总之,现在可以用纯QML编写所见即所得富文本编辑器。我们提供的文本编辑器示例不再需要C++,只需要一个基本的main()函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值