AvalonEdit 在其他格式上显示选择
AvalonEdit show selection over other formatting
我目前正在使用以下代码作为 LineTransformer
和 AvalonEdit TextEditor
。我希望能够通过选择突出显示当前的单个搜索结果,但是该选择几乎不可见,因为 DocumentColorizingTransformer
的格式优先于显示突出显示的文本。如何让突出显示的选择显示而不是格式化或在格式化之前显示?
public class ColorizeSearchResults : DocumentColorizingTransformer {
public ColorizeSearchResults() : base() {
SearchTerm = "";
MatchCase = false;
}
public string SearchTerm { get; set; }
public bool MatchCase { get; set; }
protected override void ColorizeLine(DocumentLine line) {
if (SearchTerm.Length == 0)
return;
int lineStartOffset = line.Offset;
string text = CurrentContext.Document.GetText(line);
int count = 0;
int start = 0;
int index;
while ((index = text.IndexOf(SearchTerm, start, MatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) >= 0) {
base.ChangeLinePart(
lineStartOffset + index,
lineStartOffset + index + SearchTerm.Length,
(VisualLineElement element) => {
element.TextRunProperties.SetForegroundBrush(Brushes.White);
element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);
});
start = index + 1;
count++;
}
}
}
Example of formatting showing over selection
请参阅 http://avalonedit.net/documentation/html/c06e9832-9ef0-4d65-ac2e-11f7ce9c7774.htm 了解 AvalonEdit 渲染流程。
选择层在文本之前渲染。因此,如果文本有背景,它会覆盖选择背景。幸运的是,我们可以将背景设置为 Brush.Transparent(或 Selection.Brush 和您自己的颜色的混合)。
解决方案:我修改了 SelectionColorizer 代码以将选择背景重置为透明:
class SelectionColorizerWithBackground : ColorizingTransformer
{
ICSharpCode.AvalonEdit.Editing.TextArea _textArea;
public SelectionColorizerWithBackground(
ICSharpCode.AvalonEdit.Editing.TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this._textArea = textArea;
}
protected override void Colorize(ITextRunConstructionContext context)
{
int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset;
int lineEndOffset = context.VisualLine.LastDocumentLine.Offset +
context.VisualLine.LastDocumentLine.TotalLength;
foreach (var segment in _textArea.Selection.Segments)
{
int segmentStart = segment.StartOffset;
if (segmentStart >= lineEndOffset)
continue;
int segmentEnd = segment.EndOffset;
if (segmentEnd <= lineStartOffset)
continue;
int startColumn;
if (segmentStart < lineStartOffset)
startColumn = 0;
else
startColumn = context.VisualLine.ValidateVisualColumn(
segment.StartOffset, segment.StartVisualColumn,
_textArea.Selection.EnableVirtualSpace);
int endColumn;
if (segmentEnd > lineEndOffset)
endColumn =
_textArea.Selection.EnableVirtualSpace
? int.MaxValue
: context.VisualLine
.VisualLengthWithEndOfLineMarker;
else
endColumn = context.VisualLine.ValidateVisualColumn(
segment.EndOffset, segment.EndVisualColumn,
_textArea.Selection.EnableVirtualSpace);
ChangeVisualElements(
startColumn, endColumn,
element => {
element.TextRunProperties.SetBackgroundBrush(
System.Windows.Media.Brushes.Transparent);
if (_textArea.SelectionForeground != null)
{
element.TextRunProperties.SetForegroundBrush(
_textArea.SelectionForeground);
}
});
}
}
}
要使用您应该执行以下操作的代码:
var lineTransformers = textEditor.TextArea.TextView.LineTransformers;
// Remove the original SelectionColorizer.
// Note: if you have syntax highlighting you need to do something else
// to avoid clearing other colorizers. If too complicated you can skip
// this step but to suffer a 2x performance penalty.
lineTransformers.Clear();
lineTransformers.Add(new ColorizeSearchResults());
lineTransformers.Add(
new SelectionColorizerWithBackground(textEditor.TextArea));
在我广泛尝试了我的解决方案之后,我想补充几点:
虽然我上面的其他解决方案似乎有效,但当应该平铺矩形时,您将有一些亚像素伪像。如果这是不可接受的,您可以实施 IBackgroundRenderer。 (这恰好是我选择的解决方案。)如果你想要一些代码,你可以在这里请求,但我怀疑它是否有用。
顺便说一句,因为你的问题是关于搜索结果,很可能你可以使用 https://github.com/icsharpcode/AvalonEdit/blob/697ff0d38c95c9e5a536fbc05ae2307ec9ef2a63/ICSharpCode.AvalonEdit/Search/SearchResultBackgroundRenderer.cs 未修改的(或者如果你不修改它想要圆形边框)。
您可以使用 element.BackgroundBrush = Brushes.Magenta;
而不是 element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);
。 AvalonEdit 似乎用半径为 3px 的矩形绘制背景。
从AvalonEdit 5.01开始还有RichTextColorizer。我不知道如何使用它,因为它没有在其他文件中引用。并且可能存在上一段中的(很可能不需要的)圆角矩形。
所以这是我的最终产品,几乎完全基于现有的 AvalonEdit SearchResultBackgroundRenderer
。
这与我的 post 的着色器有点不同,因为您必须手动修改搜索结果,而不是它为您做。但这也可能会节省一些计算时间。
如果您的搜索不使用正则表达式,那么您可以轻松修改 SearchResult,改为只为构造函数传入起始偏移量和长度。
/// <summary>A search result storing a match and text segment.</summary>
public class SearchResult : TextSegment {
/// <summary>The regex match for the search result.</summary>
public Match Match { get; }
/// <summary>Constructs the search result from the match.</summary>
public SearchResult(Match match) {
this.StartOffset = match.Index;
this.Length = match.Length;
this.Match = match;
}
}
/// <summary>Colorizes search results behind the selection.</summary>
public class ColorizeSearchResultsBackgroundRenderer : IBackgroundRenderer {
/// <summary>The search results to be modified.</summary>
TextSegmentCollection<SearchResult> currentResults = new TextSegmentCollection<SearchResult>();
/// <summary>Constructs the search result colorizer.</summary>
public ColorizeSearchResultsBackgroundRenderer() {
Background = new SolidColorBrush(Color.FromRgb(246, 185, 77));
Background.Freeze();
}
/// <summary>Gets the layer on which this background renderer should draw.</summary>
public KnownLayer Layer {
get {
// draw behind selection
return KnownLayer.Selection;
}
}
/// <summary>Causes the background renderer to draw.</summary>
public void Draw(TextView textView, DrawingContext drawingContext) {
if (textView == null)
throw new ArgumentNullException("textView");
if (drawingContext == null)
throw new ArgumentNullException("drawingContext");
if (currentResults == null || !textView.VisualLinesValid)
return;
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.EndOffset;
foreach (SearchResult result in currentResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;
geoBuilder.BorderThickness = 0;
geoBuilder.CornerRadius = 0;
geoBuilder.AddSegment(textView, result);
Geometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
drawingContext.DrawGeometry(Background, null, geometry);
}
}
}
/// <summary>Gets the search results for modification.</summary>
public TextSegmentCollection<SearchResult> CurrentResults {
get { return currentResults; }
}
/// <summary>Gets or sets the background brush for the search results.</summary>
public Brush Background { get; set; }
}
为了使用后台渲染器:
var searchColorizor = new ColorizeSearchResultsBackgroundRenderer();
textEditor.TextArea.TextView.BackgroundRenderers.Add(searchColorizor);
我目前正在使用以下代码作为 LineTransformer
和 AvalonEdit TextEditor
。我希望能够通过选择突出显示当前的单个搜索结果,但是该选择几乎不可见,因为 DocumentColorizingTransformer
的格式优先于显示突出显示的文本。如何让突出显示的选择显示而不是格式化或在格式化之前显示?
public class ColorizeSearchResults : DocumentColorizingTransformer {
public ColorizeSearchResults() : base() {
SearchTerm = "";
MatchCase = false;
}
public string SearchTerm { get; set; }
public bool MatchCase { get; set; }
protected override void ColorizeLine(DocumentLine line) {
if (SearchTerm.Length == 0)
return;
int lineStartOffset = line.Offset;
string text = CurrentContext.Document.GetText(line);
int count = 0;
int start = 0;
int index;
while ((index = text.IndexOf(SearchTerm, start, MatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) >= 0) {
base.ChangeLinePart(
lineStartOffset + index,
lineStartOffset + index + SearchTerm.Length,
(VisualLineElement element) => {
element.TextRunProperties.SetForegroundBrush(Brushes.White);
element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);
});
start = index + 1;
count++;
}
}
}
Example of formatting showing over selection
请参阅 http://avalonedit.net/documentation/html/c06e9832-9ef0-4d65-ac2e-11f7ce9c7774.htm 了解 AvalonEdit 渲染流程。
选择层在文本之前渲染。因此,如果文本有背景,它会覆盖选择背景。幸运的是,我们可以将背景设置为 Brush.Transparent(或 Selection.Brush 和您自己的颜色的混合)。
解决方案:我修改了 SelectionColorizer 代码以将选择背景重置为透明:
class SelectionColorizerWithBackground : ColorizingTransformer
{
ICSharpCode.AvalonEdit.Editing.TextArea _textArea;
public SelectionColorizerWithBackground(
ICSharpCode.AvalonEdit.Editing.TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this._textArea = textArea;
}
protected override void Colorize(ITextRunConstructionContext context)
{
int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset;
int lineEndOffset = context.VisualLine.LastDocumentLine.Offset +
context.VisualLine.LastDocumentLine.TotalLength;
foreach (var segment in _textArea.Selection.Segments)
{
int segmentStart = segment.StartOffset;
if (segmentStart >= lineEndOffset)
continue;
int segmentEnd = segment.EndOffset;
if (segmentEnd <= lineStartOffset)
continue;
int startColumn;
if (segmentStart < lineStartOffset)
startColumn = 0;
else
startColumn = context.VisualLine.ValidateVisualColumn(
segment.StartOffset, segment.StartVisualColumn,
_textArea.Selection.EnableVirtualSpace);
int endColumn;
if (segmentEnd > lineEndOffset)
endColumn =
_textArea.Selection.EnableVirtualSpace
? int.MaxValue
: context.VisualLine
.VisualLengthWithEndOfLineMarker;
else
endColumn = context.VisualLine.ValidateVisualColumn(
segment.EndOffset, segment.EndVisualColumn,
_textArea.Selection.EnableVirtualSpace);
ChangeVisualElements(
startColumn, endColumn,
element => {
element.TextRunProperties.SetBackgroundBrush(
System.Windows.Media.Brushes.Transparent);
if (_textArea.SelectionForeground != null)
{
element.TextRunProperties.SetForegroundBrush(
_textArea.SelectionForeground);
}
});
}
}
}
要使用您应该执行以下操作的代码:
var lineTransformers = textEditor.TextArea.TextView.LineTransformers;
// Remove the original SelectionColorizer.
// Note: if you have syntax highlighting you need to do something else
// to avoid clearing other colorizers. If too complicated you can skip
// this step but to suffer a 2x performance penalty.
lineTransformers.Clear();
lineTransformers.Add(new ColorizeSearchResults());
lineTransformers.Add(
new SelectionColorizerWithBackground(textEditor.TextArea));
在我广泛尝试了我的解决方案之后,我想补充几点:
虽然我上面的其他解决方案似乎有效,但当应该平铺矩形时,您将有一些亚像素伪像。如果这是不可接受的,您可以实施 IBackgroundRenderer。 (这恰好是我选择的解决方案。)如果你想要一些代码,你可以在这里请求,但我怀疑它是否有用。
顺便说一句,因为你的问题是关于搜索结果,很可能你可以使用 https://github.com/icsharpcode/AvalonEdit/blob/697ff0d38c95c9e5a536fbc05ae2307ec9ef2a63/ICSharpCode.AvalonEdit/Search/SearchResultBackgroundRenderer.cs 未修改的(或者如果你不修改它想要圆形边框)。
您可以使用
element.BackgroundBrush = Brushes.Magenta;
而不是element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);
。 AvalonEdit 似乎用半径为 3px 的矩形绘制背景。从AvalonEdit 5.01开始还有RichTextColorizer。我不知道如何使用它,因为它没有在其他文件中引用。并且可能存在上一段中的(很可能不需要的)圆角矩形。
所以这是我的最终产品,几乎完全基于现有的 AvalonEdit SearchResultBackgroundRenderer
。
这与我的 post 的着色器有点不同,因为您必须手动修改搜索结果,而不是它为您做。但这也可能会节省一些计算时间。
如果您的搜索不使用正则表达式,那么您可以轻松修改 SearchResult,改为只为构造函数传入起始偏移量和长度。
/// <summary>A search result storing a match and text segment.</summary>
public class SearchResult : TextSegment {
/// <summary>The regex match for the search result.</summary>
public Match Match { get; }
/// <summary>Constructs the search result from the match.</summary>
public SearchResult(Match match) {
this.StartOffset = match.Index;
this.Length = match.Length;
this.Match = match;
}
}
/// <summary>Colorizes search results behind the selection.</summary>
public class ColorizeSearchResultsBackgroundRenderer : IBackgroundRenderer {
/// <summary>The search results to be modified.</summary>
TextSegmentCollection<SearchResult> currentResults = new TextSegmentCollection<SearchResult>();
/// <summary>Constructs the search result colorizer.</summary>
public ColorizeSearchResultsBackgroundRenderer() {
Background = new SolidColorBrush(Color.FromRgb(246, 185, 77));
Background.Freeze();
}
/// <summary>Gets the layer on which this background renderer should draw.</summary>
public KnownLayer Layer {
get {
// draw behind selection
return KnownLayer.Selection;
}
}
/// <summary>Causes the background renderer to draw.</summary>
public void Draw(TextView textView, DrawingContext drawingContext) {
if (textView == null)
throw new ArgumentNullException("textView");
if (drawingContext == null)
throw new ArgumentNullException("drawingContext");
if (currentResults == null || !textView.VisualLinesValid)
return;
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.EndOffset;
foreach (SearchResult result in currentResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;
geoBuilder.BorderThickness = 0;
geoBuilder.CornerRadius = 0;
geoBuilder.AddSegment(textView, result);
Geometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
drawingContext.DrawGeometry(Background, null, geometry);
}
}
}
/// <summary>Gets the search results for modification.</summary>
public TextSegmentCollection<SearchResult> CurrentResults {
get { return currentResults; }
}
/// <summary>Gets or sets the background brush for the search results.</summary>
public Brush Background { get; set; }
}
为了使用后台渲染器:
var searchColorizor = new ColorizeSearchResultsBackgroundRenderer();
textEditor.TextArea.TextView.BackgroundRenderers.Add(searchColorizor);