Calligra/Libs/KoText/Layout Overview

From KDE Community Wiki
< Calligra‎ | Libs‎ | KoText

How does the layout work in KWord 2 ?

This likely needs review and updates as its outdated

Small reminder

In KWord, a text is stored in a QTextDocument. It spans in a KWTextFrameSet, the main frame set, that contains several KWTextFrame. Each KWTextFrame contains one KoShape. Theorically, each TextShape could contain several KoShape, but it doesn't seem to be ever done for the main frame set.

The different classes...

The root of the layout is the QAbstractTextDocumentLayout class. It's the Qt interface that any layout for QTextDocuments must implement.

The QTextDocumentLayout used in QTextEdit, that does support nested tables for instance, is a private Qt class. It means that KWord will need its own layout code for this feature for instance.

This layout part doesn't really know a lot of things about the layout of a document. There are :

- the whole document size (never used in KOffice)

- some functions to position objects on the display

- a page count property (never used in KOffice, KWord uses the KWPageManager)

- a translation from a display position to a cursor position (hitTest)

- a translation from a block to its bounding rect (never used in KOffice)

and so on, RTFM :p

Then we have the KoTextDocumentLayout class, that implements this interface.

Now, it starts being hairy. You should never forget that the text shape and kotext are used by KWord *and* by other KOffice applications. It means that features like page formatting can't be "hard-wired" in these low-level stuffs. KPresenter doesn't need pages for its text shapes, does it ? And since splitting the text in pages affects the text layout, there must be a separation between the core of the layout, that is usage-independent, and the usage-dependent part (text shape or kword...). Moreover, kotext can/could be used for something else than the textshape, so it can't rely on the textshape...

Also, in order to prevent interface freezes during heavy layouting work (for instance, on a 700 pages document), the layout work is splitted in several small parts.

So a KoTextDocumentLayout defers most of the work to a "layouter" : a class implementing KoTextDocumentLayout::LayoutState.

In KOffice, there is only one implementation of this LayoutState : the Layout class in TextShape.

So what the KoTextDocumentLayout class does is :

- positioning objects

- asking the layouter to layout the text

- storing the list of KoShape* the document will be displayed on (a document can span over several KoShape, don't forget that)

The QAbstractTextDocumentLayout functions marked previously as "never used in KOffice" are just stubs in this class.

The main functions, needed to draw & layout the document (and its layout), are done by the layouter, making the KoTextDocumentLayout code quite "simple" (well, I mean for a layout thing it's quite simple)...

So, the Layouter...

I prefer not describing it right now, it deserves its own chapter :)

That's where you will suffer.

And, finally, we have KWTextDocumentLayout, extending KoTextDocumentLayout.

It overwrites the object positionning, and add some code around the layout function that asks the layouter to do its work.

Also, instead of handling a list of KoShape*, it uses KWFrame * in a KWFrameSet (a KWTextDocumentLayout is bound to a KWFrameSet).

How is the layout done ?

What happens in KoTextDocumentLayout ?

KoTextDocumentLayout::layout is called after a layout has been scheduled. A layout is scheduled on document or layouter changes...

This function is mainly a while loop, that creates as many QTextLine as needed to store the whole text. The lines are created calling the createLine() function of the QTextLayout member of the LayoutState. As soon as the LayoutState says that there is no more text (nextParag return false) or its shape is null, the while loop ends. The lines are added to the LayoutState using the addLine function.

What is a QTextLine, what's done with that in Layout ?

A QTextLine represents one line in the document. What is done when it's created is out of purpose here, it's Qt's work, not ours...

In Layout, we call addLine. This function first checks whether the line fits the current shape. If not, it requires a new shape for this line. And that's all (ok, there is some fun with drop caps, linespacing...)

And now ?

Now, we are after layout. We are drawing. KoTextDocumentLayout just redirects this to the LayoutState, so no problem there...

And LayoutState::draw is hopefully simple : it iterates over the blocks of the document (remember ? A layout is for the whole document, and we already have QTextLines to display this text), and for each block it draws list items, any paragraph decoration that isn't supported by Qt Scribe engine, and that's all !

No, it can't be that simple.. I was only speaking about KoText here....

KWTextDocumentLayout::layout

This function is funnier. It is really big, somebody will have to work on it to add as many comments as possible, and just understand it.

Some random problems I have in the KWord/KoText code (not only layouting)

No object to represent a piece of text

Sure, there is QTextBlock. But it won't always work.

I first met this problem when I implemented a basic table of content support. Currently, a KoVariable is a stupid inline object, that stores a QString value. To implement the TOC, I had to put it in a KoVariable, so I had to reimplement the draw function of KoVariable, create a new QTextDocument for the TOC, store there the TOC content and call QTextDocument::draw. But it creates a lot of problem, it's hard to integrate it properly in the text flow...

The problem is that if the TOC is stored in a QTextBlock, the QTextBlock won't be read only... It'd be a fix for this problem, but so far I didn't find any way to have a read only QTextBlock. It doesn't seem to be possible using Qt, and in KOffice the only way I found to implement this is to block the events and change the texttool behaviour on read-only QTextBlocks.