178 lines
9.4 KiB
Plaintext
178 lines
9.4 KiB
Plaintext
|
|
<?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>
|