mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-26 06:28:35 +01:00
Compare commits
2 commits
cf3d0e8e0c
...
066ed9f8f7
Author | SHA1 | Date | |
---|---|---|---|
066ed9f8f7 | |||
b374d50815 |
9 changed files with 98 additions and 33 deletions
|
@ -32,6 +32,7 @@ Improvements
|
||||||
- Improved the way InputHandler down time calculation works
|
- Improved the way InputHandler down time calculation works
|
||||||
- Allow explicitly specifying each region for extended auto tiles
|
- Allow explicitly specifying each region for extended auto tiles
|
||||||
- Added a generic version of IGenericDataHolder.SetData
|
- Added a generic version of IGenericDataHolder.SetData
|
||||||
|
- Allow formatting codes to have an arbitrary custom width
|
||||||
- **Drastically improved StaticSpriteBatch batching performance**
|
- **Drastically improved StaticSpriteBatch batching performance**
|
||||||
- **Made GenericFont and TokenizedString support UTF-32 characters like emoji**
|
- **Made GenericFont and TokenizedString support UTF-32 characters like emoji**
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ Fixes
|
||||||
Removals
|
Removals
|
||||||
- Removed DataContract attribute from GenericDataHolder
|
- Removed DataContract attribute from GenericDataHolder
|
||||||
- Marked EnumHelper as obsolete due to its reimplementation in [DynamicEnums](https://www.nuget.org/packages/DynamicEnums)
|
- Marked EnumHelper as obsolete due to its reimplementation in [DynamicEnums](https://www.nuget.org/packages/DynamicEnums)
|
||||||
|
- Marked Code.GetReplacementString as obsolete
|
||||||
|
|
||||||
### MLEM.Ui
|
### MLEM.Ui
|
||||||
Additions
|
Additions
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
using Microsoft.Xna.Framework.Input;
|
||||||
|
using MLEM.Extensions;
|
||||||
using MLEM.Font;
|
using MLEM.Font;
|
||||||
using MLEM.Formatting;
|
using MLEM.Formatting;
|
||||||
using MLEM.Formatting.Codes;
|
using MLEM.Formatting.Codes;
|
||||||
|
@ -23,6 +25,7 @@ namespace Demos {
|
||||||
private TextFormatter formatter;
|
private TextFormatter formatter;
|
||||||
private TokenizedString tokenizedText;
|
private TokenizedString tokenizedText;
|
||||||
private GenericFont font;
|
private GenericFont font;
|
||||||
|
private bool drawBounds;
|
||||||
|
|
||||||
public TextFormattingDemo(MlemGame game) : base(game) {}
|
public TextFormattingDemo(MlemGame game) : base(game) {}
|
||||||
|
|
||||||
|
@ -56,6 +59,16 @@ namespace Demos {
|
||||||
// since the text is already center-aligned, we only need to align it on the y axis here
|
// since the text is already center-aligned, we only need to align it on the y axis here
|
||||||
var size = this.tokenizedText.Measure(this.font) * TextFormattingDemo.Scale;
|
var size = this.tokenizedText.Measure(this.font) * TextFormattingDemo.Scale;
|
||||||
var pos = new Vector2(this.GraphicsDevice.Viewport.Width / 2, (this.GraphicsDevice.Viewport.Height - size.Y) / 2);
|
var pos = new Vector2(this.GraphicsDevice.Viewport.Width / 2, (this.GraphicsDevice.Viewport.Height - size.Y) / 2);
|
||||||
|
|
||||||
|
// draw bounds, which can be toggled with B in this demo
|
||||||
|
if (this.drawBounds) {
|
||||||
|
foreach (var token in this.tokenizedText.Tokens) {
|
||||||
|
foreach (var area in token.GetArea(pos, TextFormattingDemo.Scale))
|
||||||
|
this.SpriteBatch.Draw(this.SpriteBatch.GetBlankTexture(), area, Color.Black * 0.25F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw the text itself
|
||||||
this.tokenizedText.Draw(time, this.SpriteBatch, pos, this.font, Color.White, TextFormattingDemo.Scale, 0);
|
this.tokenizedText.Draw(time, this.SpriteBatch, pos, this.font, Color.White, TextFormattingDemo.Scale, 0);
|
||||||
|
|
||||||
this.SpriteBatch.End();
|
this.SpriteBatch.End();
|
||||||
|
@ -64,6 +77,8 @@ namespace Demos {
|
||||||
public override void Update(GameTime time) {
|
public override void Update(GameTime time) {
|
||||||
// update our tokenized string to animate the animation codes
|
// update our tokenized string to animate the animation codes
|
||||||
this.tokenizedText.Update(time);
|
this.tokenizedText.Update(time);
|
||||||
|
if (this.InputHandler.IsPressed(Keys.B))
|
||||||
|
this.drawBounds = !this.drawBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Clear() {
|
public override void Clear() {
|
||||||
|
|
|
@ -110,12 +110,12 @@ 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>
|
/// <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>
|
/// <returns>The size of the string when drawn with this font</returns>
|
||||||
public Vector2 MeasureString(string text, bool ignoreTrailingSpaces = false) {
|
public Vector2 MeasureString(string text, bool ignoreTrailingSpaces = false) {
|
||||||
return this.MeasureString(new CodePointSource(text), ignoreTrailingSpaces, null);
|
return this.MeasureString(new CodePointSource(text), ignoreTrailingSpaces, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="MeasureString(string,bool)"/>
|
/// <inheritdoc cref="MeasureString(string,bool)"/>
|
||||||
public Vector2 MeasureString(StringBuilder text, bool ignoreTrailingSpaces = false) {
|
public Vector2 MeasureString(StringBuilder text, bool ignoreTrailingSpaces = false) {
|
||||||
return this.MeasureString(new CodePointSource(text), ignoreTrailingSpaces, null);
|
return this.MeasureString(new CodePointSource(text), ignoreTrailingSpaces, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -129,12 +129,12 @@ namespace MLEM.Font {
|
||||||
/// <param name="ellipsis">The characters to add to the end of the string if it is too long</param>
|
/// <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>
|
/// <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 = "") {
|
public string TruncateString(string text, float width, float scale, bool fromBack = false, string ellipsis = "") {
|
||||||
return this.TruncateString(new CodePointSource(text), width, scale, fromBack, ellipsis, null).ToString();
|
return this.TruncateString(new CodePointSource(text), width, scale, fromBack, ellipsis, null, null).ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="TruncateString(string,float,float,bool,string)"/>
|
/// <inheritdoc cref="TruncateString(string,float,float,bool,string)"/>
|
||||||
public StringBuilder TruncateString(StringBuilder text, float width, float scale, bool fromBack = false, string ellipsis = "") {
|
public StringBuilder TruncateString(StringBuilder text, float width, float scale, bool fromBack = false, string ellipsis = "") {
|
||||||
return this.TruncateString(new CodePointSource(text), width, scale, fromBack, ellipsis, null);
|
return this.TruncateString(new CodePointSource(text), width, scale, fromBack, ellipsis, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -165,21 +165,22 @@ namespace MLEM.Font {
|
||||||
/// <param name="scale">The scale to use for width measurements</param>
|
/// <param name="scale">The scale to use for width measurements</param>
|
||||||
/// <returns>The split string as an enumerable of split sections</returns>
|
/// <returns>The split string as an enumerable of split sections</returns>
|
||||||
public IEnumerable<string> SplitStringSeparate(string text, float width, float scale) {
|
public IEnumerable<string> SplitStringSeparate(string text, float width, float scale) {
|
||||||
return this.SplitStringSeparate(new CodePointSource(text), width, scale, null);
|
return this.SplitStringSeparate(new CodePointSource(text), width, scale, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="SplitStringSeparate(string,float,float)"/>
|
/// <inheritdoc cref="SplitStringSeparate(string,float,float)"/>
|
||||||
public IEnumerable<string> SplitStringSeparate(StringBuilder text, float width, float scale) {
|
public IEnumerable<string> SplitStringSeparate(StringBuilder text, float width, float scale) {
|
||||||
return this.SplitStringSeparate(new CodePointSource(text), width, scale, null);
|
return this.SplitStringSeparate(new CodePointSource(text), width, scale, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Vector2 MeasureString(CodePointSource text, bool ignoreTrailingSpaces, Func<int, GenericFont> fontFunction) {
|
internal Vector2 MeasureString(CodePointSource text, bool ignoreTrailingSpaces, Func<int, GenericFont> fontFunction, Func<int, float> extraWidthFunction) {
|
||||||
var size = Vector2.Zero;
|
var size = Vector2.Zero;
|
||||||
if (text.Length <= 0)
|
if (text.Length <= 0)
|
||||||
return size;
|
return size;
|
||||||
var xOffset = 0F;
|
var xOffset = 0F;
|
||||||
var index = 0;
|
var index = 0;
|
||||||
while (index < text.Length) {
|
while (index < text.Length) {
|
||||||
|
xOffset += extraWidthFunction?.Invoke(index) ?? 0;
|
||||||
var font = fontFunction?.Invoke(index) ?? this;
|
var font = fontFunction?.Invoke(index) ?? this;
|
||||||
var (codePoint, length) = text.GetCodePoint(index);
|
var (codePoint, length) = text.GetCodePoint(index);
|
||||||
switch (codePoint) {
|
switch (codePoint) {
|
||||||
|
@ -218,7 +219,7 @@ namespace MLEM.Font {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal StringBuilder TruncateString(CodePointSource text, float width, float scale, bool fromBack, string ellipsis, Func<int, GenericFont> fontFunction) {
|
internal StringBuilder TruncateString(CodePointSource text, float width, float scale, bool fromBack, string ellipsis, Func<int, GenericFont> fontFunction, Func<int, float> extraWidthFunction) {
|
||||||
var total = new StringBuilder();
|
var total = new StringBuilder();
|
||||||
var index = 0;
|
var index = 0;
|
||||||
while (index < text.Length) {
|
while (index < text.Length) {
|
||||||
|
@ -230,7 +231,7 @@ namespace MLEM.Font {
|
||||||
total.Append(CodePointSource.ToString(codePoint));
|
total.Append(CodePointSource.ToString(codePoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.MeasureString(new CodePointSource(total + ellipsis), false, fontFunction).X * scale >= width) {
|
if (this.MeasureString(new CodePointSource(total + ellipsis), false, fontFunction, extraWidthFunction).X * scale >= width) {
|
||||||
if (fromBack) {
|
if (fromBack) {
|
||||||
return total.Remove(0, length).Insert(0, ellipsis);
|
return total.Remove(0, length).Insert(0, ellipsis);
|
||||||
} else {
|
} else {
|
||||||
|
@ -242,7 +243,7 @@ namespace MLEM.Font {
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IEnumerable<string> SplitStringSeparate(CodePointSource text, float width, float scale, Func<int, GenericFont> fontFunction) {
|
internal IEnumerable<string> SplitStringSeparate(CodePointSource text, float width, float scale, Func<int, GenericFont> fontFunction, Func<int, float> extraWidthFunction) {
|
||||||
var currWidth = 0F;
|
var currWidth = 0F;
|
||||||
var lastSpaceIndex = -1;
|
var lastSpaceIndex = -1;
|
||||||
var widthSinceLastSpace = 0F;
|
var widthSinceLastSpace = 0F;
|
||||||
|
@ -259,7 +260,7 @@ namespace MLEM.Font {
|
||||||
} else {
|
} else {
|
||||||
var font = fontFunction?.Invoke(index) ?? this;
|
var font = fontFunction?.Invoke(index) ?? this;
|
||||||
var character = CodePointSource.ToString(codePoint);
|
var character = CodePointSource.ToString(codePoint);
|
||||||
var charWidth = font.MeasureString(character).X * scale;
|
var charWidth = (font.MeasureString(character).X + (extraWidthFunction?.Invoke(index) ?? 0)) * scale;
|
||||||
if (codePoint == ' ' || codePoint == GenericFont.Emsp || codePoint == GenericFont.Zwsp) {
|
if (codePoint == ' ' || codePoint == GenericFont.Emsp || codePoint == GenericFont.Zwsp) {
|
||||||
// remember the location of this (breaking!) space
|
// remember the location of this (breaking!) space
|
||||||
lastSpaceIndex = curr.Length;
|
lastSpaceIndex = curr.Length;
|
||||||
|
@ -298,7 +299,7 @@ namespace MLEM.Font {
|
||||||
var flippedV = (effects & SpriteEffects.FlipVertically) != 0;
|
var flippedV = (effects & SpriteEffects.FlipVertically) != 0;
|
||||||
var flippedH = (effects & SpriteEffects.FlipHorizontally) != 0;
|
var flippedH = (effects & SpriteEffects.FlipHorizontally) != 0;
|
||||||
if (flippedV || flippedH) {
|
if (flippedV || flippedH) {
|
||||||
var size = this.MeasureString(text, false, null);
|
var size = this.MeasureString(text, false, null, null);
|
||||||
if (flippedH) {
|
if (flippedH) {
|
||||||
origin.X *= -1;
|
origin.X *= -1;
|
||||||
flipX = -size.X;
|
flipX = -size.X;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
@ -68,6 +69,11 @@ namespace MLEM.Formatting.Codes {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Token.GetSelfWidth"/>
|
||||||
|
public virtual float GetSelfWidth(GenericFont font) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update this formatting code's animations etc.
|
/// Update this formatting code's animations etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -80,6 +86,7 @@ namespace MLEM.Formatting.Codes {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="font">The font that is used</param>
|
/// <param name="font">The font that is used</param>
|
||||||
/// <returns>The replacement string for this formatting code</returns>
|
/// <returns>The replacement string for this formatting code</returns>
|
||||||
|
[Obsolete("This method is deprecated. Use GetSelfWidth to add additional width to this code and DrawSelf or DrawCharacter to draw additional items.")]
|
||||||
public virtual string GetReplacementString(GenericFont font) {
|
public virtual string GetReplacementString(GenericFont font) {
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ namespace MLEM.Formatting.Codes {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string GetReplacementString(GenericFont font) {
|
public override float GetSelfWidth(GenericFont font) {
|
||||||
return GenericFont.Emsp.ToString();
|
return font.LineHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -41,12 +41,6 @@ namespace MLEM.Formatting.Codes {
|
||||||
batch.Draw(this.image.CurrentRegion, new RectangleF(pos, new Vector2(font.LineHeight * scale)), actualColor);
|
batch.Draw(this.image.CurrentRegion, new RectangleF(pos, new Vector2(font.LineHeight * scale)), actualColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
|
|
||||||
// we don't want to draw the first (space) character (in case it is set to a missing character in FNA)
|
|
||||||
return indexInToken == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -142,8 +142,11 @@ namespace MLEM.Formatting {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string StripFormatting(GenericFont font, string s, IEnumerable<Code> codes) {
|
private static string StripFormatting(GenericFont font, string s, IEnumerable<Code> codes) {
|
||||||
foreach (var code in codes)
|
foreach (var code in codes) {
|
||||||
|
#pragma warning disable CS0618
|
||||||
s = code.Regex.Replace(s, code.GetReplacementString(font));
|
s = code.Regex.Replace(s, code.GetReplacementString(font));
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,19 @@ namespace MLEM.Formatting {
|
||||||
return defaultPick;
|
return defaultPick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the width of the token itself, including all of the <see cref="Code"/> instances that this token contains.
|
||||||
|
/// Note that this method does not return the width of this token's <see cref="DisplayString"/>, but only the width that the codes themselves take up.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="font">The font to use for calculating the width.</param>
|
||||||
|
/// <returns>The width of this token itself.</returns>
|
||||||
|
public float GetSelfWidth(GenericFont font) {
|
||||||
|
var ret = 0F;
|
||||||
|
foreach (var code in this.AppliedCodes)
|
||||||
|
ret += code.GetSelfWidth(font);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws the token itself, including all of the <see cref="Code"/> instances that this token contains.
|
/// Draws the token itself, including all of the <see cref="Code"/> instances that this token contains.
|
||||||
/// Note that, to draw the token's actual string, <see cref="DrawCharacter"/> is used.
|
/// Note that, to draw the token's actual string, <see cref="DrawCharacter"/> is used.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -63,7 +64,8 @@ namespace MLEM.Formatting {
|
||||||
/// <param name="alignment">The text alignment that should be used for width calculations</param>
|
/// <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) {
|
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
|
// a split string has the same character count as the input string but with newline characters added
|
||||||
this.modifiedString = string.Join("\n", font.SplitStringSeparate(new CodePointSource(this.String), width, scale, i => this.GetFontForIndex(font, i)));
|
this.modifiedString = string.Join("\n", font.SplitStringSeparate(new CodePointSource(this.String), width, scale,
|
||||||
|
i => this.GetFontForIndex(font, i), i => this.GetSelfWidthForIndex(font, i)));
|
||||||
this.StoreModifiedSubstrings(font, alignment);
|
this.StoreModifiedSubstrings(font, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +80,8 @@ namespace MLEM.Formatting {
|
||||||
/// <param name="ellipsis">The characters to add to the end of the string if it is too long</param>
|
/// <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>
|
/// <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) {
|
public void Truncate(GenericFont font, float width, float scale, string ellipsis = "", TextAlignment alignment = TextAlignment.Left) {
|
||||||
this.modifiedString = font.TruncateString(new CodePointSource(this.String), width, scale, false, ellipsis, i => this.GetFontForIndex(font, i)).ToString();
|
this.modifiedString = font.TruncateString(new CodePointSource(this.String), width, scale, false, ellipsis,
|
||||||
|
i => this.GetFontForIndex(font, i), i => this.GetSelfWidthForIndex(font, i)).ToString();
|
||||||
this.StoreModifiedSubstrings(font, alignment);
|
this.StoreModifiedSubstrings(font, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +105,10 @@ namespace MLEM.Formatting {
|
||||||
token.InnerOffsets = new float[token.SplitDisplayString.Length - 1];
|
token.InnerOffsets = new float[token.SplitDisplayString.Length - 1];
|
||||||
var area = new List<RectangleF>();
|
var area = new List<RectangleF>();
|
||||||
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
|
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
|
||||||
var size = tokenFont.MeasureString(token.SplitDisplayString[l]);
|
var size = tokenFont.MeasureString(token.SplitDisplayString[l], !this.EndsLater(t, l));
|
||||||
|
if (l == 0)
|
||||||
|
size.X += token.GetSelfWidth(tokenFont);
|
||||||
|
|
||||||
var rect = new RectangleF(innerOffset, size);
|
var rect = new RectangleF(innerOffset, size);
|
||||||
if (!rect.IsEmpty)
|
if (!rect.IsEmpty)
|
||||||
area.Add(rect);
|
area.Add(rect);
|
||||||
|
@ -120,7 +126,7 @@ namespace MLEM.Formatting {
|
||||||
|
|
||||||
/// <inheritdoc cref="GenericFont.MeasureString(string,bool)"/>
|
/// <inheritdoc cref="GenericFont.MeasureString(string,bool)"/>
|
||||||
public Vector2 Measure(GenericFont font) {
|
public Vector2 Measure(GenericFont font) {
|
||||||
return font.MeasureString(new CodePointSource(this.DisplayString), false, i => this.GetFontForIndex(font, i));
|
return font.MeasureString(new CodePointSource(this.DisplayString), false, i => this.GetFontForIndex(font, i), i => this.GetSelfWidthForIndex(font, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -156,18 +162,23 @@ namespace MLEM.Formatting {
|
||||||
for (var t = 0; t < this.Tokens.Length; t++) {
|
for (var t = 0; t < this.Tokens.Length; t++) {
|
||||||
var token = this.Tokens[t];
|
var token = this.Tokens[t];
|
||||||
var drawFont = token.GetFont(font);
|
var drawFont = token.GetFont(font);
|
||||||
|
var selfWidth = token.GetSelfWidth(drawFont);
|
||||||
var drawColor = token.GetColor(color);
|
var drawColor = token.GetColor(color);
|
||||||
|
|
||||||
var indexInToken = 0;
|
var indexInToken = 0;
|
||||||
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
|
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
|
||||||
|
// only draw self at the start of the first line of the token!
|
||||||
|
if (indexInToken == 0) {
|
||||||
|
token.DrawSelf(time, batch, pos + innerOffset, drawFont, color, scale, depth);
|
||||||
|
innerOffset.X += selfWidth * scale;
|
||||||
|
}
|
||||||
|
|
||||||
var charIndex = 0;
|
var charIndex = 0;
|
||||||
var line = new CodePointSource(token.SplitDisplayString[l]);
|
var line = new CodePointSource(token.SplitDisplayString[l]);
|
||||||
while (charIndex < line.Length) {
|
while (charIndex < line.Length) {
|
||||||
var (codePoint, length) = line.GetCodePoint(charIndex);
|
var (codePoint, length) = line.GetCodePoint(charIndex);
|
||||||
var character = CodePointSource.ToString(codePoint);
|
var character = CodePointSource.ToString(codePoint);
|
||||||
|
|
||||||
if (indexInToken == 0)
|
|
||||||
token.DrawSelf(time, batch, pos + innerOffset, drawFont, color, scale, depth);
|
|
||||||
token.DrawCharacter(time, batch, codePoint, character, indexInToken, pos + innerOffset, drawFont, drawColor, scale, depth);
|
token.DrawCharacter(time, batch, codePoint, character, indexInToken, pos + innerOffset, drawFont, drawColor, scale, depth);
|
||||||
|
|
||||||
innerOffset.X += drawFont.MeasureString(character).X * scale;
|
innerOffset.X += drawFont.MeasureString(character).X * scale;
|
||||||
|
@ -224,14 +235,15 @@ namespace MLEM.Formatting {
|
||||||
if (alignment > TextAlignment.Left) {
|
if (alignment > TextAlignment.Left) {
|
||||||
var token = this.Tokens[tokenIndex];
|
var token = this.Tokens[tokenIndex];
|
||||||
var tokenFont = token.GetFont(defaultFont);
|
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 tokenWidth = lineIndex <= 0 ? token.GetSelfWidth(tokenFont) : 0;
|
||||||
var endsLater = lineIndex >= token.SplitDisplayString.Length - 1 && tokenIndex < this.Tokens.Length - 1;
|
var endsLater = this.EndsLater(tokenIndex, lineIndex);
|
||||||
// if the line ends in our token, we should ignore trailing white space
|
// if the line ends in our token, we should ignore trailing white space
|
||||||
var restOfLine = tokenFont.MeasureString(token.SplitDisplayString[lineIndex], !endsLater).X;
|
var restOfLine = tokenFont.MeasureString(token.SplitDisplayString[lineIndex], !endsLater).X + tokenWidth;
|
||||||
if (endsLater) {
|
if (endsLater) {
|
||||||
for (var i = tokenIndex + 1; i < this.Tokens.Length; i++) {
|
for (var i = tokenIndex + 1; i < this.Tokens.Length; i++) {
|
||||||
var other = this.Tokens[i];
|
var other = this.Tokens[i];
|
||||||
restOfLine += other.GetFont(defaultFont).MeasureString(other.SplitDisplayString[0], true).X;
|
var otherWidth = other.GetSelfWidth(defaultFont);
|
||||||
|
restOfLine += other.GetFont(defaultFont).MeasureString(other.SplitDisplayString[0], !this.EndsLater(i, 0)).X + otherWidth;
|
||||||
// if the token's split display string has multiple lines, then the line ends in it, which means we can stop
|
// if the token's split display string has multiple lines, then the line ends in it, which means we can stop
|
||||||
if (other.SplitDisplayString.Length > 1)
|
if (other.SplitDisplayString.Length > 1)
|
||||||
break;
|
break;
|
||||||
|
@ -253,5 +265,21 @@ namespace MLEM.Formatting {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float GetSelfWidthForIndex(GenericFont font, int index) {
|
||||||
|
foreach (var token in this.Tokens) {
|
||||||
|
if (index == token.Index)
|
||||||
|
return token.GetSelfWidth(font);
|
||||||
|
// if we're already beyond this token, any later tokens definitely won't match
|
||||||
|
if (token.Index > index)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool EndsLater(int tokenIndex, int lineIndex) {
|
||||||
|
// 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
|
||||||
|
return lineIndex >= this.Tokens[tokenIndex].SplitDisplayString.Length - 1 && tokenIndex < this.Tokens.Length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ using MLEM.Formatting.Codes;
|
||||||
using MLEM.Textures;
|
using MLEM.Textures;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
public class FontTests {
|
public class FontTests {
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ public class FontTests {
|
||||||
const string strg = "Lorem Ipsum <i Test> is simply dummy text of the <i Test> printing and typesetting <i Test> industry. Lorem Ipsum has been the industry's standard dummy text <i Test> ever since the <i Test> 1500s, when <i Test><i Test><i Test><i Test><i Test><i Test><i Test> an unknown printer took a galley of type and scrambled it to make a type specimen book.";
|
const string strg = "Lorem Ipsum <i Test> is simply dummy text of the <i Test> printing and typesetting <i Test> industry. Lorem Ipsum has been the industry's standard dummy text <i Test> ever since the <i Test> 1500s, when <i Test><i Test><i Test><i Test><i Test><i Test><i Test> an unknown printer took a galley of type and scrambled it to make a type specimen book.";
|
||||||
var ret = this.formatter.Tokenize(this.font, strg);
|
var ret = this.formatter.Tokenize(this.font, strg);
|
||||||
Assert.AreEqual(ret.Tokens.Length, 13);
|
Assert.AreEqual(ret.Tokens.Length, 13);
|
||||||
Assert.AreEqual(ret.DisplayString, "Lorem Ipsum \u2003 is simply dummy text of the \u2003 printing and typesetting \u2003 industry. Lorem Ipsum has been the industry's standard dummy text \u2003 ever since the \u2003 1500s, when \u2003\u2003\u2003\u2003\u2003\u2003\u2003 an unknown printer took a galley of type and scrambled it to make a type specimen book.");
|
Assert.AreEqual(ret.DisplayString, "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.");
|
||||||
Assert.AreEqual(ret.AllCodes.Length, 12);
|
Assert.AreEqual(ret.AllCodes.Length, 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +137,8 @@ public class FontTests {
|
||||||
CompareSizes("\nThis is a very simple test string");
|
CompareSizes("\nThis is a very simple test string");
|
||||||
CompareSizes("This is a very simple test string\n");
|
CompareSizes("This is a very simple test string\n");
|
||||||
CompareSizes("This is a very simple test string\n\n\n\n\n");
|
CompareSizes("This is a very simple test string\n\n\n\n\n");
|
||||||
|
CompareSizes("This is a very simple test string");
|
||||||
|
CompareSizes("This is a very simple \n test string");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|
Loading…
Reference in a new issue