mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
9.2 KiB
171 lines
9.2 KiB
<?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. |
|
The text layer also serves as container for any inline UI elements. |
|
</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> |