-
Notifications
You must be signed in to change notification settings - Fork 476
/
Copy pathText Rendering.aml
178 lines (178 loc) · 9.41 KB
/
Text Rendering.aml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<?xml version="1.0" encoding="utf-8"?>
<topic id="c06e9832-9ef0-4d65-ac2e-11f7ce9c7774" revisionNumber="1">
<developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
<summary>
<para>This document describes how the TextView class renders the text, and
how you can extend the text rendering process to add new features to the text editor.
</para>
</summary>
<introduction>
<para>The <codeEntityReference qualifyHint="true">T:ICSharpCode.AvalonEdit.Rendering.TextView</codeEntityReference>
class is the heart of AvalonEdit.
It takes care of getting the document onto the screen.</para>
<para>To do this in an extensible way, the TextView uses its own kind of model:
the <codeEntityReference>T:ICSharpCode.AvalonEdit.Rendering.VisualLine</codeEntityReference>.
Visual lines are created only for the visible part of the document.</para>
<para>
The rendering process looks like this:
<mediaLink><image xlink:href="RenderingPipeline" placement="center"/></mediaLink>
The "element generators", "line transformers" and "background renderers" are
the extension points; it is possible to add custom implementations of them to
the TextView to implement additional features in the editor.
</para>
</introduction>
<!-- Add one or more top-level section elements. These are collapsible.
If using <autoOutline /> tag, add an address attribute to identify
it so that it can be jumped to with a hyperlink. -->
<section>
<title>Lifetime of VisualLines</title>
<content>
<para>
VisualLines are only created for the visible part of the document.
Lots of actions can trigger their creation, but most commonly the creation will be
caused by the MeasureOverride method of TextView.
When the TextView is measured, it uses the height tree to determine the first
document line in the visible region. Then, it constructs and measures a VisualLine
for that first line, and repeats that with the following lines
until the visible region is filled.
</para>
<para>
The TextView caches VisualLines - when the user scrolls down, only the VisualLines
coming into view are created, the rest is reused.
The VisualLine cache can be manually invalidated using the Redraw method family;
moreover, lots of actions cause automatic invalidation:
<list class="bullet"><listItem>any change in the document will invalidate the affected VisualLines</listItem><listItem>changing the width of the TextView invalidates all VisualLines if word-wrap is enabled</listItem><listItem>changing any text editor settings (word-wrap, font size, etc.) will invalidate all VisualLines</listItem><listItem>VisualLines leaving the visible area after scrolling will be disposed</listItem></list>
In general, manual invalidation is required only if you have written a text editor extension
(BackgroundRenderer, VisualLineElementGenerator or VisualLineTransformer) that also consumes
external data - in that case, you'll have to notify the text editor that VisualLines need
to be recreated when your external data changes.
</para>
<alert class="note">
<para>If external data used by your text editor extension changes, call
<codeEntityReference qualifyHint="true">M:ICSharpCode.AvalonEdit.Rendering.TextView.Redraw</codeEntityReference>
to invalidate the VisualLine.
</para>
</alert>
<para>
Invalidating VisualLines does not cause immediate recreation of the lines!
Rather, the VisualLines are recreated when the text view is next re-measured.
While measurement in WPF normally happens with DispatcherPriority.Render,
the TextView also supports redrawing with a lower priority than that.
For example, normal text insertion causes a redraw at background priority, so that
in case the user types faster than the text view can redraw, there will be only
one redraw for multiple input actions.
</para>
<alert class="note">
<para>
The TextView will never return invalid lines to you, but you
may run into the case that the valid visual lines are not available.
</para>
<para>
What happens in this case depends on the method you are calling -
the new visual line might get created automatically,
null could be returned, or you may get a
<codeEntityReference>T:ICSharpCode.AvalonEdit.Rendering.VisualLinesInvalidException</codeEntityReference>.
</para>
<para>
You can call
<codeEntityReference qualifyHint="true">M:ICSharpCode.AvalonEdit.Rendering.TextView.EnsureVisualLines</codeEntityReference>
to make the text view create all VisualLines in the visible region.
</para>
</alert>
</content>
</section>
<section>
<title>Building visual line elements</title>
<content>
<para>
As a first step, the VisualLineElementGenerators are used to produce elements. The
room in between the elements returned from the generators is filled with text elements.
Then, the VisualLine assigns the VisualColumn and RelativeTextOffset properties of the line elements.
</para>
<para>
For example, a line contains the text "Hello, World".
The user has enabled "ShowSpaces", so the text editor should show a little dot instead of the space.
In this case, the SingleCharacterElementGenerator, which is responsible for ShowSpaces, will produce
a "SpaceTextElement" for the space character. Because no other generators are interested in the line,
the remaining strings "Hello," and "World" will be represented by VisualLineText elements.
</para>
</content>
</section>
<section>
<title>Transforming visual line elements</title>
<content>
<para>
After that, the IVisualLineTransformers are used to modify the produced line elements. Transformers
must not add elements, but they may split existing elements, e.g. to colorize only parts of an
element. When splitting elements (or somehow modifying the elements collection), care must be taken
that the VisualColumn,VisualLine,RelativeTextOffset and DocumentLength properties stay correct.
</para>
<para>
The ColorizingTransformer base class provides helper methods for splitting, so the derived class
can simply say "color this section in that color".
</para>
<para>
The DocumentColorizingTransformer extends the ColorizingTransformer and additionally
allows highlighting on per DocumentLine, coloring text segments (instead of directly
working with visual line elements).
</para>
</content>
</section>
<section>
<title>Constructing TextLines</title>
<content>
<para>
After building the visual line elements, the TextLines for the visual line are constructed.
A visual line may result in multiple text lines when word wrapping is active or when special visual
line elements force a line break.
Building text lines:
The text line construction is done by a WPF TextFormatter.
The VisualLineTextSource will take the visual line elements and build WPF TextRuns from it,
while the WPF TextFormatter takes care of word wrapping etc.
VisualLineElements are requested to produce TextRuns for their full or a partial length.
The TextView will take care to measure any inline UI elements in the visual lines.
</para>
</content>
</section>
<section>
<title>Rest of the Rendering</title>
<content>
<para>
After the visible region is filled, the TextView updates the heights stored in the document lines to
the measured heights. This way, scrolling takes account for word-wrapping.
The constructed text lines are stored for the arrange and render steps.
Now, finally, the measure step is complete.
</para>
<para>
The WPF arrange step doesn't have much work to do:
It just arranges inline UI elements at their position inside the text.
</para>
<para>
The actual rendering does not happen directly in the TextView, but in its
various layers.
</para>
<para>
These are the predefined layers:
<list class="bullet">
<listItem>Background layer: renders the background colors associated with the visual elements</listItem>
<listItem>Selection layer: renders the background of the selection</listItem>
<listItem>Text layer: renders the TextLines that were constructed during the Measure step.
Starting with AvalonEdit 4.1, the TextLayer uses child elements to draw the text: one DrawingVisual for each VisualLine.
</listItem>
<listItem>
Immediately after the text layer, any inline UI elements are placed as if they were separate layers.
</listItem>
<listItem>Caret layer: renders a blinking caret</listItem></list>
It's also possible to insert new layers into the TextView using the
<codeEntityReference qualifyHint="true">M:ICSharpCode.AvalonEdit.Rendering.TextView.InsertLayer(System.Windows.UIElement,ICSharpCode.AvalonEdit.Rendering.KnownLayer,ICSharpCode.AvalonEdit.Rendering.LayerInsertionPosition)</codeEntityReference>
method.
This allows adding custom interactive components to the editor
while being in full control of the Z-Order.
</para>
</content>
</section>
<relatedTopics>
<codeEntityReference>T:ICSharpCode.AvalonEdit.Rendering.TextView</codeEntityReference>
</relatedTopics>
</developerConceptualDocument>
</topic>