|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options;
internal sealed class OptionPageSearchHandler
{
public static readonly Brush HighlightForeground = SystemColors.HighlightTextBrush;
public static readonly Brush HighlightBackground = SystemColors.HighlightBrush;
private readonly ContentControl _control;
private readonly string _originalContent;
private readonly int _accessKeyIndex;
private readonly string _content;
public OptionPageSearchHandler(ContentControl control, string originalContent)
{
_control = control;
_originalContent = originalContent;
// Currently only support one access key, and no underscores
Debug.Assert(_originalContent.Split('_').Length <= 2);
_accessKeyIndex = _originalContent.IndexOf('_');
// We strip out the access key so it doesn't interupt search, and because we have to handle displaying it ourselves anyway.
// Since we strip it out, we also don't need to worry about the access key being the character after the underscore
_content = _originalContent.Replace("_", "");
}
public bool TryHighlightSearchString(string searchTerm)
{
var index = _content.IndexOf(searchTerm, StringComparison.CurrentCultureIgnoreCase);
if (index == -1 || string.IsNullOrWhiteSpace(searchTerm))
{
if (_accessKeyIndex != -1)
{
// Unregister and let the content control handle access keys
AccessKeyManager.Unregister(_content[_accessKeyIndex].ToString(), _control);
}
_control.Content = _originalContent;
return false;
}
if (_accessKeyIndex != -1)
{
// Because we are overriding the content entirely, we have to handle access keys
AccessKeyManager.Register(_content[_accessKeyIndex].ToString(), _control);
}
_control.Content = CreateHighlightingTextRun(index, searchTerm.Length);
return true;
}
public void EnsureVisible()
{
_control.BringIntoView();
}
private TextBlock CreateHighlightingTextRun(int highlightStart, int length)
{
var textBlock = new TextBlock();
AddTextRun(textBlock, 0, highlightStart, highlight: false);
AddTextRun(textBlock, highlightStart, length, highlight: true);
var highlightEnd = highlightStart + length;
AddTextRun(textBlock, highlightEnd, _content.Length - highlightEnd, highlight: false);
return textBlock;
}
private void AddTextRun(TextBlock textBlock, int start, int length, bool highlight)
{
if (length <= 0)
return;
// If the access key is in this run, then we actually need to add three runs
if (_accessKeyIndex >= start && _accessKeyIndex < start + length)
{
var firstPartLength = _accessKeyIndex - start;
var lastPartLength = length - firstPartLength - 1;
if (firstPartLength > 0)
textBlock.Inlines.Add(CreateRun(start, firstPartLength, highlight, underline: false));
textBlock.Inlines.Add(CreateRun(_accessKeyIndex, 1, highlight, underline: true));
if (lastPartLength > 0)
textBlock.Inlines.Add(CreateRun(_accessKeyIndex + 1, lastPartLength, highlight, underline: false));
}
else
{
textBlock.Inlines.Add(CreateRun(start, length, highlight, underline: false));
}
}
private Run CreateRun(int start, int length, bool highlight, bool underline)
{
var run = new Run(_content.Substring(start, length));
if (highlight)
{
run.Background = HighlightBackground;
run.Foreground = HighlightForeground;
}
if (underline)
run.TextDecorations.Add(TextDecorations.Underline);
return run;
}
}
|