QML Multi-line Texts Handling

From Qt Wiki
Revision as of 16:34, 14 January 2015 by Maintenance script (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

English

QML Multi-line Texts Handling

Introduction

There are many situations where we need to handle relatively lengthy fragments of text, for example in help systems. Qt framework has its Assistance help system [developer.qt.nokia.com] implemented in Qt C++. As another example we may consider learning content for which we need a flexible interface to multi-line blocks of text.

Don’t confuse this with text processing; we are considering options for easy structuring of text in blocks and their rendering through a flexible QML interface.

At first look, QML with its Text element, is oriented to handling short text portions like labels, titles, dialogs, etc. Combining rich text formatting options in QML with its software architecture possibilities we may have interesting solutions.

The starting point is the requirement to separate text blocks from basic QML code, encapsulating the text blocks in autonomous units, and providing a powerful interface. It is clear that a text block can change very often – editing of the text, translation in other languages and so on. The solution is to form a QML component and instantiate it in the basic application code in places where we need it.

Text Component

Let us recall how we define a component in QML [doc.qt.nokia.com]. The next snippet demonstrates our text component, which is used in all examples that follow. The text component is placed in a file named TextTemplate.qml. That is the name of our text component, which is used in main QML file. This name appears in next illustrating code fragments. You have needed explanations in comments.

TextTemplate.qml

Text Component Handling

As a first solution (and trivial one) for text blocks structuring we may define as many components as many text blocks we have. In the application code we include components in places where we need to display respective text blocks.

The second approach uses the possibility to bind a value to a QML element property at a later time – not when the element is defined. And we want to do this programmatically. In our text component we bind text property to another top level property (note it is of type string). This way the text component becomes a generic entity where different text property values could be bound when the component is used in the basic QML code. In other words, we use the text component as a template, instantiate it in different places and bind different text property values. That is what we call programmatically altering of text values.

The next point is how we could assign values in QML? As we know QML is a declarative language and it has no explicit operation assignment you may know from other procedural (imperative) languages. In QML we could bind a value in property definition. We may think about this as initializing of the property – the initial value could be changed later. This is similar as well for pre-defined properties as for own defined ones.

Another option is to construct a script block where assignment operation is allowed. There a few cases where we could define a script block. For example the value of an attached property of a signal could be a script block and what we will apply in the following examples.

Following the above ideas for text property altering we could have several approaches as explained bellow.

Direct Binding of a Value

The name of the main QML file is LargeTexts.qml.

LargeTexts.qml

Use of Properties Defined at Top Level

Bind a Value Using QML Binding Element

Use onCompleted Attached Property

Apply a JavaScript Inline Function

The third approach uses JavaScript constructs to define text blocks in a separate .js file. We recall that text property accepts as values anything that result in to a string. For example text property value could be an expression evaluated to a string. Particularly this value could be a string element of a JavaScript array.  The scheme is as follows:

Do you really need an array? If you prefer you may have a variable for each text block:

How to Format a Multi-line Text

As you know QML supports plain text as well as rich one. There is an essential difference here. QML Text element has built-in properties for text formatting like font.bold, font.pointSize, etc. When we use them they apply to the whole string being value of the text property. If you want to format a text fragment in your text block or even a separate word (symbol) you should use QML rich text mode. This of course supposes you have a background in HTML. Be aware that QML supports a sub-set of HTML/CSS – consult [http] for supported HTML/CSS constructs.
The QML runtime includes a JavaScript engine and supports JavaScript, HTML and CSS. For instance, if you create a HTML file with your favorite WYSIWYG HTML editor (without deep HTML coding understanding) it is most probably that QML will accept this file when you define it as a text block. For example this HTML file from my HTML editor works with QML:

Now return to the case of plain text. Suppose we have a text header and a paragraph and want to format them differently – for example the header to be bold and colored. The simplest solution is to have two separate Text elements – one for the header and one for the paragraph. The better solution is to
encapsulate somehow the both constructs. To do this we may nest Text elements like this:

When we are dealing with a multi-line text in QML an important issue is how text is wrapped. Use QML Text element property wrapMode. It takes several enumerated values [doc.qt.nokia.com] . For example you may use Text.WordWrap as it is done in our text component example. In this mode the text is wrapped on words boundaries. You have to define the Text element width property if want wrapMode to take effect.

You may fix the length of each line in a multi-line text using so called hard breaks. In a plain text use control symbol \n to mark line end. In a rich text we use <br> tag.

This approach is not recommended especially in cases the text changes often. Instead define the width property of the Text element and apply wrapMode property to have text block adapted to the text frame automatically.

Mixing QML, JavaScript and HTML/CSS constructs it is very easy to run into some syntax issues with some control symbols. The first problem comes from double quotes. They are used in QML as well in HTML/CSS also. The simplest solution seems to be replacing double quotes in text property with single ones or vice-verse. Of course you may use escape sequence \symbol also like this:

When composing your text block in Qt Quick or other editors you will want to break each line of the text to fit on the screen without any scrolling. If such a block is defined in a script element we have to keep to rules for string splitting in multiple lines. Otherwise you get a message like “Parse error”. The proposed solution is: before breaking a text line add a control symbol for string continuation – \. Probably you know it from other programming languages.

Make Your Text Block Flickable

It is obvious that for bigger text blocks we should make them somehow scrollable. QML offers a very useful construct Flickable for these purposes. Recall the Flickable construct from [doc.qt.nokia.com]. The main question here is what should be the width and height dimensions of the flickable area. You may fix them, but it is not recommendable. The width
property depends on the page design rather. Inherit the width value from Flickable parents. For height property we may use properties binding and calculate Flickable height programmatically. Assume we have three Text elements included in Flickable element that have IDs like this: id:element1,
id:element2 and id:element3. Then Flickable element height property could be calculated as:


Note: You may get a demo file illustrating the discussed constructs. Download it at Syncho Server [bit.ly]

Categories: