mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-25 14:08:34 +01:00
improved performance of SplitString and re-added Zwsp compatibility
This commit is contained in:
parent
538fd08d8a
commit
e7ab8fefe8
3 changed files with 67 additions and 30 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
@ -21,6 +22,11 @@ namespace MLEM.Font {
|
||||||
/// 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"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const char Nbsp = '\u00A0';
|
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"/>.
|
||||||
|
/// </summary>
|
||||||
|
public const char Zwsp = '\u200B';
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bold version of this font.
|
/// The bold version of this font.
|
||||||
|
@ -108,6 +114,9 @@ namespace MLEM.Font {
|
||||||
case Nbsp:
|
case Nbsp:
|
||||||
xOffset += this.MeasureChar(' ').X;
|
xOffset += this.MeasureChar(' ').X;
|
||||||
break;
|
break;
|
||||||
|
case Zwsp:
|
||||||
|
// don't add width for a zero-width space
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
xOffset += this.MeasureChar(c).X;
|
xOffset += this.MeasureChar(c).X;
|
||||||
break;
|
break;
|
||||||
|
@ -161,38 +170,47 @@ 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, containing newline characters at each new line</returns>
|
/// <returns>The split string, containing newline characters at each new line</returns>
|
||||||
public string SplitString(string text, float width, float scale) {
|
public string SplitString(string text, float width, float scale) {
|
||||||
var total = new StringBuilder();
|
var ret = new StringBuilder();
|
||||||
foreach (var line in text.Split('\n')) {
|
var currWidth = 0F;
|
||||||
var curr = new StringBuilder();
|
var lastSpaceIndex = -1;
|
||||||
foreach (var word in line.Split(' ')) {
|
var widthSinceLastSpace = 0F;
|
||||||
if (this.MeasureString(word).X * scale >= width) {
|
for (var i = 0; i < text.Length; i++) {
|
||||||
if (curr.Length > 0) {
|
var c = text[i];
|
||||||
total.Append(curr).Append('\n');
|
if (c == '\n') {
|
||||||
curr.Clear();
|
// split at pre-defined new lines
|
||||||
}
|
ret.Append(c);
|
||||||
var wordBuilder = new StringBuilder();
|
lastSpaceIndex = -1;
|
||||||
for (var i = 0; i < word.Length; i++) {
|
widthSinceLastSpace = 0;
|
||||||
wordBuilder.Append(word[i]);
|
currWidth = 0;
|
||||||
if (this.MeasureString(wordBuilder.ToString()).X * scale >= width) {
|
|
||||||
total.Append(wordBuilder.ToString(0, wordBuilder.Length - 1)).Append('\n');
|
|
||||||
wordBuilder.Remove(0, wordBuilder.Length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
curr.Append(wordBuilder).Append(' ');
|
|
||||||
} else {
|
} else {
|
||||||
curr.Append(word).Append(' ');
|
var cWidth = this.MeasureChar(c).X * scale;
|
||||||
if (this.MeasureString(curr.ToString()).X * scale >= width) {
|
if (c == ' ' || c == OneEmSpace || c == Zwsp) {
|
||||||
var len = curr.Length - word.Length - 1;
|
// remember the location of this space
|
||||||
if (len > 0) {
|
lastSpaceIndex = ret.Length;
|
||||||
total.Append(curr.ToString(0, len)).Append('\n');
|
widthSinceLastSpace = 0;
|
||||||
curr.Remove(0, len);
|
} else if (currWidth + cWidth >= width) {
|
||||||
|
// check if this line contains a space
|
||||||
|
if (lastSpaceIndex < 0) {
|
||||||
|
// if there is no last space, the word is longer than a line so we split here
|
||||||
|
ret.Append('\n');
|
||||||
|
currWidth = 0;
|
||||||
|
} else {
|
||||||
|
// split after the last space
|
||||||
|
ret.Insert(lastSpaceIndex + 1, '\n');
|
||||||
|
// we need to restore the width accumulated since the last space for the new line
|
||||||
|
currWidth = widthSinceLastSpace;
|
||||||
|
}
|
||||||
|
widthSinceLastSpace = 0;
|
||||||
|
lastSpaceIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add current character
|
||||||
|
currWidth += cWidth;
|
||||||
|
widthSinceLastSpace += cWidth;
|
||||||
|
ret.Append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return ret.ToString();
|
||||||
}
|
|
||||||
total.Append(curr).Append('\n');
|
|
||||||
}
|
|
||||||
return total.ToString(0, total.Length - 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -72,8 +73,8 @@ namespace MLEM.Formatting {
|
||||||
// if we're within the bounds of the token's substring, append to the new substring
|
// if we're within the bounds of the token's substring, append to the new substring
|
||||||
if (index >= token.Index)
|
if (index >= token.Index)
|
||||||
ret.Append(this.splitString[i]);
|
ret.Append(this.splitString[i]);
|
||||||
// if the current char is not a newline, we simulate length increase
|
// if the current char is not an added newline, we simulate length increase
|
||||||
if (this.splitString[i] != '\n') {
|
if (this.splitString[i] != '\n' || this.String[index] == '\n') {
|
||||||
if (index >= token.Index)
|
if (index >= token.Index)
|
||||||
length++;
|
length++;
|
||||||
index++;
|
index++;
|
||||||
|
|
|
@ -79,6 +79,24 @@ namespace Tests {
|
||||||
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
|
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNewlineSplit() {
|
||||||
|
var formatted = this.formatter.Tokenize(this.font,
|
||||||
|
"This is a pretty long line with regular <c Blue>content</c> that will be split.\nNow this is a new line with additional regular <c Blue>content</c> that is forced into a new line.");
|
||||||
|
formatted.Split(this.font, 65, 0.1F);
|
||||||
|
Assert.AreEqual(formatted.DisplayString, "This is a pretty long line with \nregular content that will be \nsplit.\nNow this is a new line with \nadditional regular content that \nis forced into a new line.");
|
||||||
|
|
||||||
|
var tokens = new[] {
|
||||||
|
"This is a pretty long line with \nregular ",
|
||||||
|
"content",
|
||||||
|
" that will be \nsplit.\nNow this is a new line with \nadditional regular ",
|
||||||
|
"content",
|
||||||
|
" that \nis forced into a new line."
|
||||||
|
};
|
||||||
|
for (var i = 0; i < tokens.Length; i++)
|
||||||
|
Assert.AreEqual(formatted.Tokens[i].DisplayString, tokens[i]);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestMacros() {
|
public void TestMacros() {
|
||||||
this.formatter.Macros.Add(new Regex("<testmacro>"), (f, m, r) => "<test1>");
|
this.formatter.Macros.Add(new Regex("<testmacro>"), (f, m, r) => "<test1>");
|
||||||
|
|
Loading…
Reference in a new issue