Uppercasing in AvalonEdit

一世执手 提交于 2019-12-06 06:27:12

This is difficult, because upper-casing can change the mapping between displayed characters and the document (visual columns vs. document offsets).

For example, the single character 'ß' (German sharp s) exists only as a lower-case letter, and gets converted to the two-character string "SS" when calling string.ToUpper(). Editing this text is tricky: we can't allow the user to replace only one of the 'S', as the underlying document only contains the 'ß'.

A simple solution is to use the char.ToUpper() method instead, enforcing a one-to-one mapping between original and uppercase characters. This will keep letters like 'ß' unchanged.

In AvalonEdit 4.2, only two transformations are allowed on the already generated VisualLineElements:

  • Change the text run properties, e.g. font size, text color, etc.
  • Split a VisualLineElement in two - this is used internally by ChangeLinePart() so that properties can be changed for a text portion.

This means it is not possible to do a text replacement in a colorizer, you need to implement this using a VisualLineElementGenerator.

/// <summary>
/// Makes all text after a colon (until the end of line) upper-case.
/// </summary>
public class UppercaseGenerator : VisualLineElementGenerator
{
    public override int GetFirstInterestedOffset(int startOffset)
    {
        TextDocument document = CurrentContext.Document;
        int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
        for (int i = startOffset; i < endOffset; i++) {
            char c = document.GetCharAt(i);
            if (c == ':')
                return i + 1;
        }
        return -1;
    }

    public override VisualLineElement ConstructElement(int offset)
    {
        DocumentLine line = CurrentContext.Document.GetLineByOffset(offset);
        return new UppercaseText(CurrentContext.VisualLine, line.EndOffset - offset);
    }

    /// <summary>
    /// Displays a portion of the document text, but upper-cased.
    /// </summary>
    class UppercaseText : VisualLineText
    {
        public UppercaseText(VisualLine parentVisualLine, int length) : base(parentVisualLine, length)
        {
        }

        protected override VisualLineText CreateInstance(int length)
        {
            return new UppercaseText(ParentVisualLine, length);
        }

        public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            int relativeOffset = startVisualColumn - VisualColumn;
            StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset + relativeOffset, DocumentLength - relativeOffset);
            char[] uppercase = new char[text.Count];
            for (int i = 0; i < text.Count; i++) {
                uppercase[i] = char.ToUpper(text.Text[text.Offset + i]);
            }
            return new TextCharacters(uppercase, 0, uppercase.Length, this.TextRunProperties);
        }
    }
}

In AvalonEdit 4.3.0.8868, I added the method VisualLine.ReplaceElement(). This can be used to replace the default VisualText elements with UppercaseText elements within a line transformer (colorizer).

Note that it is also possible to implement support for 'ß' being displayed as 'SS'. For that, you would have to implement your own copy of VisualLineText instead of just overriding the existing one. Then you can use a visual length that differs from the document length. The GetRelativeOffset and GetVisualColumns methods would be used to provide the mapping between the document and visual coordinates.


There is another option you could use: small caps.

// in the colorizer:
ChangeLinePart(start, end, e => e.TextRunProperties.SetTypographyProperties(new CapsTypography()));

// helper class
class CapsTypography : DefaultTextRunTypographyProperties
{
    public override FontCapitals Capitals {
        get { return FontCapitals.SmallCaps; }
    }
}

However, WPF will render small caps only when using an OpenType font that supports them. In my testing, Cambria worked with small caps, most other fonts don't. Also, the SetTypographyProperties method and DefaultTextRunTypographyProperties class require AvalonEdit 4.3.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!