How to display text with subscripts or superscripts in the Title Bar?

坚强是说给别人听的谎言 提交于 2020-05-12 02:44:06

问题


I would like to be able to show subscripted text in the Title Bar of a Windows Form or WPF Window. The reason for this is simple. Our project team has written a molecule editor:

Instead of just displaying its name, 'ACME', we would like to show something like:

ACME - Editing C6H12Cl

where the text is subscripted (and possibly superscripted) and whether the control is shown in a Windows Forms or WPF host.


回答1:


The original question was asking how to insert a RichTextBox in a Form's Caption, to show a chemical formula.
It could be done, of course: you could take a look at DwmExtendFrameIntoClientArea and the Docs here: Custom Window Frame Using DWM and a number of SO questions.

But it looked like overshooting, here. There's a simpler alternative: make use of the existing Unicode SubScript symbols (it's an Unicode cathegory) to reproduce the formula.

These are the base Subscript and SuperScript Unicode codepoints of the Hindu-Arabic numerals:

char[] subScriptNumbers = {
    '\u2080', '\u2081', '\u2082', '\u2083', '\u2084',
    '\u2085', '\u2086', '\u2087', '\u2088', '\u2089'
};
char[] superScriptNumbers = {
    '\u2070', '\u00B9', '\u00B2', '\u00B3', '\u2074',
    '\u2075', '\u2076', '\u2077', '\u2078', '\u2079'
};

As hinted in comments, the simple formula: C6H12Cl can be transformed to C₆H₁₂Cl, mapping the numbers to the corresponding Unicode values in the SubScript range. For example:

this.Text = string.Concat("C6H12Cl".Select(c => char.IsDigit(c) ? subScriptNumbers[c-48] : c));

Or, since the SubScript codepoints are sequential (the SuperScript ones are not):

const int subScriptBase = 0x2080;
string chem = "C6H12Cl";
// Or this.Title, in WPF
this.Text = chem.Aggregate(new StringBuilder(), (sb, c) => 
    sb.Append(char.IsDigit(c) ? (char)(subScriptBase + c - 48) : c)).ToString();

Since it appears that there's someone interested, I'm proposing a slightly more complex parser (which uses the same logic), to produce different kinds of formulas:

The class shown here can convert sequences of SubScript/SuperScript numerals or letters, using a simple notation (similar to the notation of the Markup used by Wikipedia):

SuperScript: [+:symbols]          A[+:12]    => A¹²  
SubScript:   [-:symbols]          A[-:12]    => A₁₂
Fraction:    [f:symbols/symbols]  A·[f:x/12] => A·ˣ⁄₁₂

For example:

string formula = "N[+:(x+2)] · H[+:3] · γLog[-:e] + δ· [f:n11/x]";
// Or this.Text, in WinForms
this.Title = UniSubSup.Parse(formula);

will print:

N⁽ˣ⁺²⁾·H³·γLogₑ + δ·ⁿ¹¹⁄ₓ

Note1:
When a markup contains spaces, it's skipped: hence [+:(x+2)] is parsed, while [+:(x + 2)] is not (in case these brackets should be ignored).

Note2:
I did not include all the letters because not all fonts support all the codepoints in the SubScript and SuperScript cathegories. The Subscript n (\u2099), which is relatively common (e.g., Logn), is not available in most of font types (since sub/superScripting is generated by different means).
Some (very few) Fonts do have these glyphs. WebSites like fileformat.info can provide these informations.

public class UniSubSup
{
    const char joiner =       '\u200D';
    const char nonJoiner =    '\u200C';
    const char fraction =     '\u2044';
    const char solidusShort = '\u0337';
    const char solidusLong =  '\u0338';

    protected internal static string sub(string s) => 
        s.Aggregate(new StringBuilder(), (sb, c) => sb.Append(subScripts[c])).ToString();

    protected internal static string sup(string s) => 
        s.Aggregate(new StringBuilder(), (sb, c) => sb.Append(superScripts[c])).ToString();

    protected internal static string fract(string str)
    {
        var sb = new StringBuilder();
        var parts = str.Split('/');
        parts[0].Aggregate(sb, (s, c) => sb.Append(superScripts[c]));
        sb.Append(fraction);
        parts[1].Aggregate(sb, (s, c) => sb.Append(subScripts[c]));
        return sb.ToString();
    }

    protected internal static Dictionary<string, Func<string, string>> actions =
        new Dictionary<string, Func<string, string>>()
        {
            ["-"] = (s) => sub(s),
            ["+"] = (s) => sup(s),
            ["f"] = (s) => fract(s),
        };

    static RegexOptions options = RegexOptions.Singleline | RegexOptions.Compiled;

    public static string Parse(string input)
    {
        string pattern = @"\[(\D{1}):(\S\/?\S*?)\]";
        var matches = Regex.Matches(input, pattern, options);
        StringBuilder result = new StringBuilder(input);
        foreach (Match m in matches)
        {
            result = result.Replace(m.Value, actions[m.Groups[1].Value](m.Groups[2].Value));
        }
        return result.ToString();
    }

    internal static Dictionary<char, char> superScripts = new Dictionary<char, char>()
    {
        ['0'] = '\u2070', ['1'] = '\u00B9', ['2'] = '\u00B2', ['3'] = '\u00B3',
        ['4'] = '\u2074', ['5'] = '\u2075', ['6'] = '\u2076', ['7'] = '\u2077',
        ['8'] = '\u2078', ['9'] = '\u2079',
        ['+'] = '\u207A', ['-'] = '\u207B', ['='] = '\u207C',
        ['('] = '\u207D', [')'] = '\u207E',
        ['e'] = '\u1D49', ['n'] = '\u207F', ['x'] = '\u02E3' 
    };

    internal static Dictionary<char, char> subScripts = new Dictionary<char, char>()
    {
        ['0'] = '\u2080', ['1'] = '\u2081', ['2'] = '\u2082', ['3'] = '\u2083',
        ['4'] = '\u2084', ['5'] = '\u2085', ['6'] = '\u2086', ['7'] = '\u2087',
        ['8'] = '\u2088', ['9'] = '\u2089',
        ['+'] = '\u208A', ['-'] = '\u208B', ['='] = '\u208C',
        ['('] = '\u208D', [')'] = '\u208E', ['/'] = '\u2044',
        ['e'] = '\u2091', ['n'] = '\u2099', ['x'] = '\u2093'
    };
}



回答2:


Update on question raised by OP

I am the "poor other sod" who works on this project who has used the above to implement it.

The result is shown below, we now have subscripted characters in the title bar happy days.

This is the code that did the trick in conjunction with the definition of the number to unicode translation seen above

this.Text = "ACME - Editing" + string.Concat("C4H9NO2".Select(c => char.IsDigit(c) ? subScriptNumbers[c-48] : c));



来源:https://stackoverflow.com/questions/59435306/how-to-display-text-with-subscripts-or-superscripts-in-the-title-bar

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