mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-24 21:48:35 +01:00
Compare commits
4 commits
ad1d6a864e
...
a11a63c067
Author | SHA1 | Date | |
---|---|---|---|
a11a63c067 | |||
f445aba45c | |||
293602269b | |||
53b93a34f8 |
12 changed files with 202 additions and 84 deletions
|
@ -23,6 +23,7 @@ Improvements
|
|||
- Added Padding.Empty
|
||||
- Throw an exception when text formatter macros resolve recursively too many times
|
||||
- Allow using StaticSpriteBatch for AutoTiling
|
||||
- Made TextFormatter string size based on the currently active font rather than the default one
|
||||
|
||||
Fixes
|
||||
- Fixed some end-of-line inconsistencies when using the Right text alignment
|
||||
|
@ -31,6 +32,7 @@ Fixes
|
|||
Additions
|
||||
- Allow specifying a maximum amount of characters for a TextField
|
||||
- Added a multiline editing mode to TextField
|
||||
- Added a formatting code to allow for inline font changes
|
||||
|
||||
Improvements
|
||||
- *Made Image ScaleToImage take ui scale into account*
|
||||
|
|
|
@ -34,6 +34,13 @@
|
|||
/processorParam:TextureFormat=Compressed
|
||||
/build:Fonts/TestFontItalic.spritefont
|
||||
|
||||
#begin Fonts/MonospacedFont.spritefont
|
||||
/importer:FontDescriptionImporter
|
||||
/processor:FontDescriptionProcessor
|
||||
/processorParam:PremultiplyAlpha=True
|
||||
/processorParam:TextureFormat=Compressed
|
||||
/build:Fonts/MonospacedFont.spritefont
|
||||
|
||||
#begin Textures/Anim.png
|
||||
/importer:TextureImporter
|
||||
/processor:TextureProcessor
|
||||
|
|
BIN
Demos/Content/Fonts/JetBrainsMono-Regular.ttf
Normal file
BIN
Demos/Content/Fonts/JetBrainsMono-Regular.ttf
Normal file
Binary file not shown.
60
Demos/Content/Fonts/MonospacedFont.spritefont
Normal file
60
Demos/Content/Fonts/MonospacedFont.spritefont
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file contains an xml description of a font, and will be read by the XNA
|
||||
Framework Content Pipeline. Follow the comments to customize the appearance
|
||||
of the font in your game, and to change the characters which are available to draw
|
||||
with.
|
||||
-->
|
||||
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
|
||||
<Asset Type="Graphics:FontDescription">
|
||||
|
||||
<!--
|
||||
Modify this string to change the font that will be imported.
|
||||
-->
|
||||
<FontName>JetBrainsMono-Regular.ttf</FontName>
|
||||
|
||||
<!--
|
||||
Size is a float value, measured in points. Modify this value to change
|
||||
the size of the font.
|
||||
-->
|
||||
<Size>32</Size>
|
||||
|
||||
<!--
|
||||
Spacing is a float value, measured in pixels. Modify this value to change
|
||||
the amount of spacing in between characters.
|
||||
-->
|
||||
<Spacing>0</Spacing>
|
||||
|
||||
<!--
|
||||
UseKerning controls the layout of the font. If this value is true, kerning information
|
||||
will be used when placing characters.
|
||||
-->
|
||||
<UseKerning>true</UseKerning>
|
||||
|
||||
<!--
|
||||
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
|
||||
and "Bold, Italic", and are case sensitive.
|
||||
-->
|
||||
<Style>Regular</Style>
|
||||
|
||||
<!--
|
||||
If you uncomment this line, the default character will be substituted if you draw
|
||||
or measure text that contains characters which were not included in the font.
|
||||
-->
|
||||
<DefaultCharacter>*</DefaultCharacter>
|
||||
|
||||
<!--
|
||||
CharacterRegions control what letters are available in the font. Every
|
||||
character from Start to End will be built and made available for drawing. The
|
||||
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
|
||||
character set. The characters are ordered according to the Unicode standard.
|
||||
See the documentation for more information.
|
||||
-->
|
||||
<CharacterRegions>
|
||||
<CharacterRegion>
|
||||
<Start> </Start>
|
||||
<End>ɏ</End>
|
||||
</CharacterRegion>
|
||||
</CharacterRegions>
|
||||
</Asset>
|
||||
</XnaContent>
|
|
@ -47,7 +47,8 @@ namespace Demos {
|
|||
CheckboxTexture = new NinePatch(new TextureRegion(this.testTexture, 24, 8, 16, 16), 4),
|
||||
CheckboxCheckmark = new TextureRegion(this.testTexture, 24, 0, 8, 8),
|
||||
RadioTexture = new NinePatch(new TextureRegion(this.testTexture, 16, 0, 8, 8), 3),
|
||||
RadioCheckmark = new TextureRegion(this.testTexture, 32, 0, 8, 8)
|
||||
RadioCheckmark = new TextureRegion(this.testTexture, 32, 0, 8, 8),
|
||||
AdditionalFonts = {{"Monospaced", new GenericSpriteFont(LoadContent<SpriteFont>("Fonts/MonospacedFont"))}}
|
||||
};
|
||||
var untexturedStyle = new UntexturedStyle(this.SpriteBatch) {
|
||||
TextScale = style.TextScale,
|
||||
|
@ -87,7 +88,7 @@ namespace Demos {
|
|||
this.root.AddChild(new VerticalSpace(3));
|
||||
|
||||
// a paragraph with formatting codes. To see them all or to add more, check the TextFormatting class
|
||||
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Paragraphs can also contain <c Blue>formatting codes</c>, including colors and <i>text styles</i>. The names of all <c Orange>MonoGame Colors</c> can be used, as well as the codes <i>Italic</i>, <b>Bold</b>, <s>Drop Shadow'd</s> and <s><c Pink>mixed formatting</s></c>. \n<i>Even <c #ff611f82>inline custom colors</c> work!</i>"));
|
||||
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Paragraphs can also contain <c Blue>formatting codes</c>, including colors and <i>text styles</i>. The names of all <c Orange>MonoGame Colors</c> can be used, as well as the codes <i>Italic</i>, <b>Bold</b>, <s>Drop Shadow'd</s> and <s><c Pink>mixed formatting</s></c>. You can also add additional fonts for things like\n<f Monospaced>void Code() {\n // Code\n}</f>\n<i>Even <c #ff611f82>inline custom colors</c> work!</i>"));
|
||||
|
||||
// adding some custom image formatting codes
|
||||
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Additionally, you can create custom formatting codes that contain <i Grass> images and more!"));
|
||||
|
|
|
@ -4,7 +4,7 @@ The **MLEM** package contains a simple text formatting system that supports colo
|
|||
|
||||
Text formatting makes use of [generic fonts](font_extensions.md).
|
||||
|
||||
It should also be noted that [MLEM.Ui](ui.md)'s `Paragraph`s support text formatting out of the box.
|
||||
It should also be noted that [MLEM.Ui](ui.md)'s `Paragraph` supports text formatting out of the box.
|
||||
|
||||
## Formatting codes
|
||||
To format your text, you can insert *formatting codes* into it. Almost all of these codes are single letters surrounded by `<>`, and some formatting codes can accept additional parameters after their letter representation.
|
||||
|
@ -16,6 +16,10 @@ By default, the following formatting options are available:
|
|||
- Underlined and strikethrough text using `<u>` and `<st>`, respectively. Reset using `</u>` and `</st>`.
|
||||
- A wobbly sine wave animation using `<a wobbly>`. Optional parameters for the wobble's intensity and height are accepted: `<a wobbly 10 0.25>`. Reset using `</a>`.
|
||||
|
||||
When using [MLEM.Ui](ui.md)'s `Paragraph`, these additional formatting options are available by default:
|
||||
- Hoverable and clickable links using `<l Url>`. Note that this code does not automatically change the color of the text. Reset using `</l>`.
|
||||
- Inline font changes using `<f FontName>`, with custom fonts gathered from `UiStyle.AdditionalFonts`. Reset using `</f>`.
|
||||
|
||||
## Getting your text ready
|
||||
To get your text ready for rendering with formatting codes, it has to be tokenized. For that, you need to create a new text formatter first. Additionally, you need to have a [generic font](font_extensions.md) ready:
|
||||
```cs
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MLEM.Font;
|
||||
using MLEM.Formatting;
|
||||
|
@ -194,6 +195,10 @@ namespace MLEM.Ui.Style {
|
|||
/// Note that this sound is only played if the callbacks have any subscribers.
|
||||
/// </summary>
|
||||
public SoundEffectInfo ActionSound;
|
||||
/// <summary>
|
||||
/// A set of additional fonts that can be used for the <c><f FontName></c> formatting code
|
||||
/// </summary>
|
||||
public Dictionary<string, GenericFont> AdditionalFonts = new Dictionary<string, GenericFont>();
|
||||
|
||||
}
|
||||
}
|
|
@ -223,6 +223,8 @@ namespace MLEM.Ui {
|
|||
this.TextFormatter = new TextFormatter();
|
||||
this.TextFormatter.Codes.Add(new Regex("<l(?: ([^>]+))?>"), (f, m, r) => new LinkCode(m, r, 1 / 16F, 0.85F,
|
||||
t => this.Controls.MousedElement is Paragraph.Link l1 && l1.Token == t || this.Controls.TouchedElement is Paragraph.Link l2 && l2.Token == t));
|
||||
this.TextFormatter.Codes.Add(new Regex("<f ([^>]+)>"), (_, m, r) => new FontCode(m, r,
|
||||
f => this.Style.AdditionalFonts != null && this.Style.AdditionalFonts.TryGetValue(m.Groups[1].Value, out var c) ? c : f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
@ -15,17 +16,17 @@ namespace MLEM.Font {
|
|||
/// <summary>
|
||||
/// This field holds the unicode representation of a one em space.
|
||||
/// This is a character that isn't drawn, but has the same width as <see cref="LineHeight"/>.
|
||||
/// Whereas a regular <see cref="SpriteFont"/> would have to explicitly support this character for width calculations, generic fonts implicitly support it in <see cref="MeasureString"/>.
|
||||
/// Whereas a regular <see cref="SpriteFont"/> would have to explicitly support this character for width calculations, generic fonts implicitly support it in <see cref="MeasureString(string,bool)"/>.
|
||||
/// </summary>
|
||||
public const char OneEmSpace = '\u2003';
|
||||
/// <summary>
|
||||
/// This field holds the unicode representation of a non-breaking space.
|
||||
/// Whereas a regular <see cref="SpriteFont"/> would have to explicitly support this character for width calculations, generic fonts implicitly support it in <see cref="MeasureString"/>.
|
||||
/// Whereas a regular <see cref="SpriteFont"/> would have to explicitly support this character for width calculations, generic fonts implicitly support it in <see cref="MeasureString(string,bool)"/>.
|
||||
/// </summary>
|
||||
public const char Nbsp = '\u00A0';
|
||||
/// <summary>
|
||||
/// This field holds the unicode representation of a zero-width space.
|
||||
/// Whereas a regular <see cref="SpriteFont"/> would have to explicitly support this character for width calculations and string splitting, generic fonts implicitly support it in <see cref="MeasureString"/> and <see cref="SplitString"/>.
|
||||
/// Whereas a regular <see cref="SpriteFont"/> would have to explicitly support this character for width calculations and string splitting, generic fonts implicitly support it in <see cref="MeasureString(string,bool)"/> and <see cref="SplitString"/>.
|
||||
/// </summary>
|
||||
public const char Zwsp = '\u200B';
|
||||
|
||||
|
@ -46,8 +47,8 @@ namespace MLEM.Font {
|
|||
public abstract float LineHeight { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Measures the width of the given character with the default scale for use in <see cref="MeasureString"/>.
|
||||
/// Note that this method does not support <see cref="Nbsp"/>, <see cref="Zwsp"/> and <see cref="OneEmSpace"/> for most generic fonts, which is why <see cref="MeasureString"/> should be used even for single characters.
|
||||
/// Measures the width of the given character with the default scale for use in <see cref="MeasureString(string,bool)"/>.
|
||||
/// Note that this method does not support <see cref="Nbsp"/>, <see cref="Zwsp"/> and <see cref="OneEmSpace"/> for most generic fonts, which is why <see cref="MeasureString(string,bool)"/> should be used even for single characters.
|
||||
/// </summary>
|
||||
/// <param name="c">The character whose width to calculate</param>
|
||||
/// <returns>The width of the given character with the default scale</returns>
|
||||
|
@ -88,44 +89,7 @@ namespace MLEM.Font {
|
|||
/// <param name="ignoreTrailingSpaces">Whether trailing whitespace should be ignored in the returned size, causing the end of each line to be effectively trimmed</param>
|
||||
/// <returns>The size of the string when drawn with this font</returns>
|
||||
public Vector2 MeasureString(string text, bool ignoreTrailingSpaces = false) {
|
||||
var size = Vector2.Zero;
|
||||
if (text.Length <= 0)
|
||||
return size;
|
||||
var xOffset = 0F;
|
||||
for (var i = 0; i < text.Length; i++) {
|
||||
switch (text[i]) {
|
||||
case '\n':
|
||||
xOffset = 0;
|
||||
size.Y += this.LineHeight;
|
||||
break;
|
||||
case OneEmSpace:
|
||||
xOffset += this.LineHeight;
|
||||
break;
|
||||
case Nbsp:
|
||||
xOffset += this.MeasureChar(' ');
|
||||
break;
|
||||
case Zwsp:
|
||||
// don't add width for a zero-width space
|
||||
break;
|
||||
case ' ':
|
||||
if (ignoreTrailingSpaces && IsTrailingSpace(text, i)) {
|
||||
// if this is a trailing space, we can skip remaining spaces too
|
||||
i = text.Length - 1;
|
||||
break;
|
||||
}
|
||||
xOffset += this.MeasureChar(' ');
|
||||
break;
|
||||
default:
|
||||
xOffset += this.MeasureChar(text[i]);
|
||||
break;
|
||||
}
|
||||
// increase x size if this line is the longest
|
||||
if (xOffset > size.X)
|
||||
size.X = xOffset;
|
||||
}
|
||||
// include the last line's height too!
|
||||
size.Y += this.LineHeight;
|
||||
return size;
|
||||
return this.MeasureString(text, ignoreTrailingSpaces, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -139,30 +103,13 @@ namespace MLEM.Font {
|
|||
/// <param name="ellipsis">The characters to add to the end of the string if it is too long</param>
|
||||
/// <returns>The truncated string, or the same string if it is shorter than the maximum width</returns>
|
||||
public string TruncateString(string text, float width, float scale, bool fromBack = false, string ellipsis = "") {
|
||||
var total = new StringBuilder();
|
||||
var ellipsisWidth = this.MeasureString(ellipsis).X * scale;
|
||||
for (var i = 0; i < text.Length; i++) {
|
||||
if (fromBack) {
|
||||
total.Insert(0, text[text.Length - 1 - i]);
|
||||
} else {
|
||||
total.Append(text[i]);
|
||||
}
|
||||
|
||||
if (this.MeasureString(total.ToString()).X * scale + ellipsisWidth >= width) {
|
||||
if (fromBack) {
|
||||
return total.Remove(0, 1).Insert(0, ellipsis).ToString();
|
||||
} else {
|
||||
return total.Remove(total.Length - 1, 1).Append(ellipsis).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return total.ToString();
|
||||
return this.TruncateString(text, width, scale, fromBack, ellipsis, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string to a given maximum width, adding newline characters between each line.
|
||||
/// Also splits long words and supports zero-width spaces and takes into account existing newline characters in the passed <paramref name="text"/>.
|
||||
/// See <see cref="SplitStringSeparate"/> for a method that differentiates between existing newline characters and splits due to maximum width.
|
||||
/// See <see cref="SplitStringSeparate(string,float,float)"/> for a method that differentiates between existing newline characters and splits due to maximum width.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to split into multiple lines</param>
|
||||
/// <param name="width">The maximum width that each line should have</param>
|
||||
|
@ -182,6 +129,73 @@ namespace MLEM.Font {
|
|||
/// <param name="scale">The scale to use for width measurements</param>
|
||||
/// <returns>The split string as an enumerable of split sections</returns>
|
||||
public IEnumerable<string> SplitStringSeparate(string text, float width, float scale) {
|
||||
return this.SplitStringSeparate(text, width, scale, null);
|
||||
}
|
||||
|
||||
internal Vector2 MeasureString(string text, bool ignoreTrailingSpaces, Func<int, GenericFont> fontFunction) {
|
||||
var size = Vector2.Zero;
|
||||
if (text.Length <= 0)
|
||||
return size;
|
||||
var xOffset = 0F;
|
||||
for (var i = 0; i < text.Length; i++) {
|
||||
var font = fontFunction?.Invoke(i) ?? this;
|
||||
switch (text[i]) {
|
||||
case '\n':
|
||||
xOffset = 0;
|
||||
size.Y += font.LineHeight;
|
||||
break;
|
||||
case OneEmSpace:
|
||||
xOffset += font.LineHeight;
|
||||
break;
|
||||
case Nbsp:
|
||||
xOffset += font.MeasureChar(' ');
|
||||
break;
|
||||
case Zwsp:
|
||||
// don't add width for a zero-width space
|
||||
break;
|
||||
case ' ':
|
||||
if (ignoreTrailingSpaces && IsTrailingSpace(text, i)) {
|
||||
// if this is a trailing space, we can skip remaining spaces too
|
||||
i = text.Length - 1;
|
||||
break;
|
||||
}
|
||||
xOffset += font.MeasureChar(' ');
|
||||
break;
|
||||
default:
|
||||
xOffset += font.MeasureChar(text[i]);
|
||||
break;
|
||||
}
|
||||
// increase x size if this line is the longest
|
||||
if (xOffset > size.X)
|
||||
size.X = xOffset;
|
||||
}
|
||||
// include the last line's height too!
|
||||
size.Y += (fontFunction?.Invoke(text.Length - 1) ?? this).LineHeight;
|
||||
return size;
|
||||
}
|
||||
|
||||
internal string TruncateString(string text, float width, float scale, bool fromBack, string ellipsis, Func<int, GenericFont> fontFunction) {
|
||||
var total = new StringBuilder();
|
||||
for (var i = 0; i < text.Length; i++) {
|
||||
if (fromBack) {
|
||||
total.Insert(0, text[text.Length - 1 - i]);
|
||||
} else {
|
||||
total.Append(text[i]);
|
||||
}
|
||||
|
||||
var font = fontFunction?.Invoke(i) ?? this;
|
||||
if (font.MeasureString(total + ellipsis).X * scale >= width) {
|
||||
if (fromBack) {
|
||||
return total.Remove(0, 1).Insert(0, ellipsis).ToString();
|
||||
} else {
|
||||
return total.Remove(total.Length - 1, 1).Append(ellipsis).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return total.ToString();
|
||||
}
|
||||
|
||||
internal IEnumerable<string> SplitStringSeparate(string text, float width, float scale, Func<int, GenericFont> fontFunction) {
|
||||
var currWidth = 0F;
|
||||
var lastSpaceIndex = -1;
|
||||
var widthSinceLastSpace = 0F;
|
||||
|
@ -195,7 +209,8 @@ namespace MLEM.Font {
|
|||
widthSinceLastSpace = 0;
|
||||
currWidth = 0;
|
||||
} else {
|
||||
var cWidth = this.MeasureString(c.ToCachedString()).X * scale;
|
||||
var font = fontFunction?.Invoke(i) ?? this;
|
||||
var cWidth = font.MeasureString(c.ToCachedString()).X * scale;
|
||||
if (c == ' ' || c == OneEmSpace || c == Zwsp) {
|
||||
// remember the location of this (breaking!) space
|
||||
lastSpaceIndex = curr.Length;
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace MLEM.Formatting {
|
|||
this.Codes.Add(new Regex("<u>"), (f, m, r) => new UnderlineCode(m, r, 1 / 16F, 0.85F));
|
||||
this.Codes.Add(new Regex("<st>"), (f, m, r) => new UnderlineCode(m, r, 1 / 16F, 0.55F));
|
||||
this.Codes.Add(new Regex("</(s|u|st|l)>"), (f, m, r) => new ResetFormattingCode(m, r));
|
||||
this.Codes.Add(new Regex("</(b|i)>"), (f, m, r) => new FontCode(m, r, null));
|
||||
this.Codes.Add(new Regex("</(b|i|f)>"), (f, m, r) => new FontCode(m, r, null));
|
||||
|
||||
// color codes
|
||||
foreach (var c in typeof(Color).GetProperties()) {
|
||||
|
|
|
@ -60,8 +60,13 @@ namespace MLEM.Formatting {
|
|||
/// </summary>
|
||||
/// <param name="defaultPick">The default color, if none is specified</param>
|
||||
/// <returns>The color to render with</returns>
|
||||
public Color? GetColor(Color defaultPick) {
|
||||
return this.AppliedCodes.Select(c => c.GetColor(defaultPick)).FirstOrDefault(c => c.HasValue);
|
||||
public Color GetColor(Color defaultPick) {
|
||||
foreach (var code in this.AppliedCodes) {
|
||||
var color = code.GetColor(defaultPick);
|
||||
if (color.HasValue)
|
||||
return color.Value;
|
||||
}
|
||||
return defaultPick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -70,7 +75,12 @@ namespace MLEM.Formatting {
|
|||
/// <param name="defaultPick">The default font, if none is specified</param>
|
||||
/// <returns>The font to render with</returns>
|
||||
public GenericFont GetFont(GenericFont defaultPick) {
|
||||
return this.AppliedCodes.Select(c => c.GetFont(defaultPick)).FirstOrDefault(f => f != null);
|
||||
foreach (var code in this.AppliedCodes) {
|
||||
var font = code.GetFont(defaultPick);
|
||||
if (font != null)
|
||||
return font;
|
||||
}
|
||||
return defaultPick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -59,14 +59,14 @@ namespace MLEM.Formatting {
|
|||
/// <param name="alignment">The text alignment that should be used for width calculations</param>
|
||||
public void Split(GenericFont font, float width, float scale, TextAlignment alignment = TextAlignment.Left) {
|
||||
// a split string has the same character count as the input string but with newline characters added
|
||||
this.modifiedString = font.SplitString(this.String, width, scale);
|
||||
this.modifiedString = string.Join("\n", font.SplitStringSeparate(this.String, width, scale, i => this.GetFontForIndex(font, i)));
|
||||
this.StoreModifiedSubstrings(font, alignment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Truncates this tokenized string, removing any additional characters that exceed the length from the displayed string.
|
||||
/// Note that a tokenized string can be re-truncated without losing any of its actual data, as this operation merely modifies the <see cref="DisplayString"/>.
|
||||
/// <seealso cref="GenericFont.TruncateString"/>
|
||||
/// <seealso cref="GenericFont.TruncateString(string,float,float,bool,string)"/>
|
||||
/// </summary>
|
||||
/// <param name="font">The font to use for width calculations</param>
|
||||
/// <param name="width">The maximum width, in display pixels based on the font and scale</param>
|
||||
|
@ -74,13 +74,13 @@ namespace MLEM.Formatting {
|
|||
/// <param name="ellipsis">The characters to add to the end of the string if it is too long</param>
|
||||
/// <param name="alignment">The text alignment that should be used for width calculations</param>
|
||||
public void Truncate(GenericFont font, float width, float scale, string ellipsis = "", TextAlignment alignment = TextAlignment.Left) {
|
||||
this.modifiedString = font.TruncateString(this.String, width, scale, false, ellipsis);
|
||||
this.modifiedString = font.TruncateString(this.String, width, scale, false, ellipsis, i => this.GetFontForIndex(font, i));
|
||||
this.StoreModifiedSubstrings(font, alignment);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GenericFont.MeasureString(string,bool)"/>
|
||||
public Vector2 Measure(GenericFont font) {
|
||||
return font.MeasureString(this.DisplayString);
|
||||
return font.MeasureString(this.DisplayString, false, i => this.GetFontForIndex(font, i));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -115,18 +115,18 @@ namespace MLEM.Formatting {
|
|||
var innerOffset = new Vector2(this.initialInnerOffset * scale, 0);
|
||||
for (var t = 0; t < this.Tokens.Length; t++) {
|
||||
var token = this.Tokens[t];
|
||||
var drawFont = token.GetFont(font) ?? font;
|
||||
var drawColor = token.GetColor(color) ?? color;
|
||||
var drawFont = token.GetFont(font);
|
||||
var drawColor = token.GetColor(color);
|
||||
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
|
||||
var line = token.SplitDisplayString[l];
|
||||
for (var i = 0; i < line.Length; i++) {
|
||||
var c = line[i];
|
||||
if (l == 0 && i == 0)
|
||||
token.DrawSelf(time, batch, pos + innerOffset, font, color, scale, depth);
|
||||
token.DrawSelf(time, batch, pos + innerOffset, drawFont, color, scale, depth);
|
||||
|
||||
var cString = c.ToCachedString();
|
||||
token.DrawCharacter(time, batch, c, cString, i, pos + innerOffset, drawFont, drawColor, scale, depth);
|
||||
innerOffset.X += font.MeasureString(cString).X * scale;
|
||||
innerOffset.X += drawFont.MeasureString(cString).X * scale;
|
||||
}
|
||||
// only split at a new line, not between tokens!
|
||||
if (l < token.SplitDisplayString.Length - 1) {
|
||||
|
@ -183,17 +183,18 @@ namespace MLEM.Formatting {
|
|||
var innerOffset = new Vector2(this.initialInnerOffset, 0);
|
||||
for (var t = 0; t < this.Tokens.Length; t++) {
|
||||
var token = this.Tokens[t];
|
||||
var tokenFont = token.GetFont(font);
|
||||
token.InnerOffsets = new float[token.SplitDisplayString.Length - 1];
|
||||
var area = new List<RectangleF>();
|
||||
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
|
||||
var size = font.MeasureString(token.SplitDisplayString[l]);
|
||||
var size = tokenFont.MeasureString(token.SplitDisplayString[l]);
|
||||
var rect = new RectangleF(innerOffset, size);
|
||||
if (!rect.IsEmpty)
|
||||
area.Add(rect);
|
||||
|
||||
if (l < token.SplitDisplayString.Length - 1) {
|
||||
innerOffset.X = token.InnerOffsets[l] = this.GetInnerOffsetX(font, t, l + 1, alignment);
|
||||
innerOffset.Y += font.LineHeight;
|
||||
innerOffset.Y += tokenFont.LineHeight;
|
||||
} else {
|
||||
innerOffset.X += size.X;
|
||||
}
|
||||
|
@ -202,24 +203,26 @@ namespace MLEM.Formatting {
|
|||
}
|
||||
}
|
||||
|
||||
private float GetInnerOffsetX(GenericFont font, int tokenIndex, int lineIndex, TextAlignment alignment) {
|
||||
private float GetInnerOffsetX(GenericFont defaultFont, int tokenIndex, int lineIndex, TextAlignment alignment) {
|
||||
if (alignment > TextAlignment.Left) {
|
||||
var token = this.Tokens[tokenIndex];
|
||||
var tokenFont = token.GetFont(defaultFont);
|
||||
// if we're the last line in our line array, then we don't contain a line split, so the line ends in a later token
|
||||
var endsLater = lineIndex >= token.SplitDisplayString.Length - 1;
|
||||
// if the line ends in our token, we should ignore trailing white space
|
||||
var restOfLine = font.MeasureString(token.SplitDisplayString[lineIndex], !endsLater).X;
|
||||
var restOfLine = tokenFont.MeasureString(token.SplitDisplayString[lineIndex], !endsLater).X;
|
||||
if (endsLater) {
|
||||
for (var i = tokenIndex + 1; i < this.Tokens.Length; i++) {
|
||||
var other = this.Tokens[i];
|
||||
var otherFont = other.GetFont(defaultFont);
|
||||
if (other.SplitDisplayString.Length > 1) {
|
||||
// the line ends in this token (so we also ignore trailing whitespaces)
|
||||
restOfLine += font.MeasureString(other.SplitDisplayString[0], true).X;
|
||||
restOfLine += otherFont.MeasureString(other.SplitDisplayString[0], true).X;
|
||||
break;
|
||||
} else {
|
||||
// the line doesn't end in this token (or it's the last token), so add it fully
|
||||
var lastToken = i >= this.Tokens.Length - 1;
|
||||
restOfLine += font.MeasureString(other.DisplayString, lastToken).X;
|
||||
restOfLine += otherFont.MeasureString(other.DisplayString, lastToken).X;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,5 +233,14 @@ namespace MLEM.Formatting {
|
|||
return 0;
|
||||
}
|
||||
|
||||
private GenericFont GetFontForIndex(GenericFont font, int index) {
|
||||
foreach (var token in this.Tokens) {
|
||||
index -= token.Substring.Length;
|
||||
if (index <= 0)
|
||||
return token.GetFont(font);
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue