diff --git a/CHANGELOG.md b/CHANGELOG.md
index c020eca..4a912b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,9 @@ Jump to version:
## 5.3.0 (Unreleased)
### MLEM
+Additions
+- Added StringBuilder overloads to GenericFont
+
Improvements
- Generify GenericFont's string drawing
diff --git a/MLEM.Extended/Font/GenericBitmapFont.cs b/MLEM.Extended/Font/GenericBitmapFont.cs
index db785b4..8b9a494 100644
--- a/MLEM.Extended/Font/GenericBitmapFont.cs
+++ b/MLEM.Extended/Font/GenericBitmapFont.cs
@@ -38,8 +38,8 @@ namespace MLEM.Extended.Font {
}
///
- protected override void DrawChar(SpriteBatch batch, char c, string cString, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
- batch.DrawString(this.Font, cString, position, color, rotation, origin, scale, effects, layerDepth);
+ protected override void DrawChar(SpriteBatch batch, string cString, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
+ batch.DrawString(this.Font, cString, position, color, rotation, Vector2.Zero, scale, effects, layerDepth);
}
}
diff --git a/MLEM.Extended/Font/GenericStashFont.cs b/MLEM.Extended/Font/GenericStashFont.cs
index 4afb137..e36d280 100644
--- a/MLEM.Extended/Font/GenericStashFont.cs
+++ b/MLEM.Extended/Font/GenericStashFont.cs
@@ -38,8 +38,8 @@ namespace MLEM.Extended.Font {
}
///
- protected override void DrawChar(SpriteBatch batch, char c, string cString, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
- this.Font.DrawText(batch, cString, position, color, scale, rotation, origin, layerDepth);
+ protected override void DrawChar(SpriteBatch batch,string cString, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
+ this.Font.DrawText(batch, cString, position, color, scale, rotation, Vector2.Zero, layerDepth);
}
}
diff --git a/MLEM/Font/GenericFont.cs b/MLEM/Font/GenericFont.cs
index 8ea788b..fa060a6 100644
--- a/MLEM/Font/GenericFont.cs
+++ b/MLEM/Font/GenericFont.cs
@@ -59,20 +59,23 @@ namespace MLEM.Font {
/// Note that this method is only called internally.
///
/// The sprite batch to draw with.
- /// The character which will be drawn.
/// A string representation of the character which will be drawn.
/// The drawing location on screen.
/// A color mask.
/// A rotation of this character.
- /// Center of the rotation. 0,0 by default.
/// A scaling of this character.
/// Modificators for drawing. Can be combined.
/// A depth of the layer of this character.
- protected abstract void DrawChar(SpriteBatch batch, char c, string cString, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth);
+ protected abstract void DrawChar(SpriteBatch batch, string cString, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth);
+
+ ///
+ public void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
+ this.DrawString(batch, new CharSource(text), position, color, rotation, origin, scale, effects, layerDepth);
+ }
///
public void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
- this.DrawString(batch, text.ToString(), position, color, rotation, origin, scale, effects, layerDepth);
+ this.DrawString(batch, new CharSource(text), position, color, rotation, origin, scale, effects, layerDepth);
}
///
@@ -95,64 +98,6 @@ namespace MLEM.Font {
this.DrawString(batch, text, position, color, 0, Vector2.Zero, Vector2.One, SpriteEffects.None, 0);
}
- ///
- public void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
- var (flipX, flipY) = Vector2.Zero;
- var flippedV = (effects & SpriteEffects.FlipVertically) != 0;
- var flippedH = (effects & SpriteEffects.FlipHorizontally) != 0;
- if (flippedV || flippedH) {
- var (w, h) = this.MeasureString(text);
- if (flippedH) {
- origin.X *= -1;
- flipX = -w;
- }
- if (flippedV) {
- origin.Y *= -1;
- flipY = this.LineHeight - h;
- }
- }
-
- var trans = Matrix.Identity;
- if (rotation == 0) {
- trans.M11 = flippedH ? -scale.X : scale.X;
- trans.M22 = flippedV ? -scale.Y : scale.Y;
- trans.M41 = (flipX - origin.X) * trans.M11 + position.X;
- trans.M42 = (flipY - origin.Y) * trans.M22 + position.Y;
- } else {
- var sin = (float) Math.Sin(rotation);
- var cos = (float) Math.Cos(rotation);
- trans.M11 = (flippedH ? -scale.X : scale.X) * cos;
- trans.M12 = (flippedH ? -scale.X : scale.X) * sin;
- trans.M21 = (flippedV ? -scale.Y : scale.Y) * -sin;
- trans.M22 = (flippedV ? -scale.Y : scale.Y) * cos;
- trans.M41 = (flipX - origin.X) * trans.M11 + (flipY - origin.Y) * trans.M21 + position.X;
- trans.M42 = (flipX - origin.X) * trans.M12 + (flipY - origin.Y) * trans.M22 + position.Y;
- }
-
- var offset = Vector2.Zero;
- for (var i = 0; i < text.Length; i++) {
- var c = text[i];
- if (c == '\n') {
- offset.X = 0;
- offset.Y += this.LineHeight;
- continue;
- }
-
- var cString = c.ToCachedString();
- var (cW, cH) = this.MeasureString(cString);
-
- var charPos = offset;
- if (flippedH)
- charPos.X += cW;
- if (flippedV)
- charPos.Y += cH - this.LineHeight;
- Vector2.Transform(ref charPos, ref trans, out charPos);
-
- this.DrawChar(batch, c, cString, charPos, color, rotation, Vector2.Zero, scale, effects, layerDepth);
- offset.X += cW;
- }
- }
-
///
/// Measures the width of the given string when drawn with this font's underlying font.
/// This method uses internally to calculate the size of known characters and calculates additional characters like , and .
@@ -162,7 +107,7 @@ namespace MLEM.Font {
/// Whether trailing whitespace should be ignored in the returned size, causing the end of each line to be effectively trimmed
/// The size of the string when drawn with this font
public Vector2 MeasureString(string text, bool ignoreTrailingSpaces = false) {
- return this.MeasureString(text, ignoreTrailingSpaces, null);
+ return this.MeasureString(new CharSource(text), ignoreTrailingSpaces, null);
}
///
@@ -176,7 +121,12 @@ namespace MLEM.Font {
/// The characters to add to the end of the string if it is too long
/// The truncated string, or the same string if it is shorter than the maximum width
public string TruncateString(string text, float width, float scale, bool fromBack = false, string ellipsis = "") {
- return this.TruncateString(text, width, scale, fromBack, ellipsis, null);
+ return this.TruncateString(new CharSource(text), width, scale, fromBack, ellipsis, null).ToString();
+ }
+
+ ///
+ public StringBuilder TruncateString(StringBuilder text, float width, float scale, bool fromBack = false, string ellipsis = "") {
+ return this.TruncateString(new CharSource(text), width, scale, fromBack, ellipsis, null);
}
///
@@ -192,20 +142,30 @@ namespace MLEM.Font {
return string.Join("\n", this.SplitStringSeparate(text, width, scale));
}
+ ///
+ public string SplitString(StringBuilder text, float width, float scale) {
+ return string.Join("\n", this.SplitStringSeparate(text, width, scale));
+ }
+
///
/// Splits a string to a given maximum width and returns each split section as a separate string.
/// Note that existing new lines are taken into account for line length, but not split in the resulting strings.
- /// This method differs from in that it differentiates between pre-existing newline characters and splits due to maximum width.
+ /// This method differs from in that it differentiates between pre-existing newline characters and splits due to maximum width.
///
/// The text to split into multiple lines
/// The maximum width that each line should have
/// The scale to use for width measurements
/// The split string as an enumerable of split sections
public IEnumerable SplitStringSeparate(string text, float width, float scale) {
- return this.SplitStringSeparate(text, width, scale, null);
+ return this.SplitStringSeparate(new CharSource(text), width, scale, null);
}
- internal Vector2 MeasureString(string text, bool ignoreTrailingSpaces, Func fontFunction) {
+ ///
+ public IEnumerable SplitStringSeparate(StringBuilder text, float width, float scale) {
+ return this.SplitStringSeparate(new CharSource(text), width, scale, null);
+ }
+
+ internal Vector2 MeasureString(CharSource text, bool ignoreTrailingSpaces, Func fontFunction) {
var size = Vector2.Zero;
if (text.Length <= 0)
return size;
@@ -247,7 +207,7 @@ namespace MLEM.Font {
return size;
}
- internal string TruncateString(string text, float width, float scale, bool fromBack, string ellipsis, Func fontFunction) {
+ internal StringBuilder TruncateString(CharSource text, float width, float scale, bool fromBack, string ellipsis, Func fontFunction) {
var total = new StringBuilder();
for (var i = 0; i < text.Length; i++) {
if (fromBack) {
@@ -259,16 +219,16 @@ namespace MLEM.Font {
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();
+ return total.Remove(0, 1).Insert(0, ellipsis);
} else {
- return total.Remove(total.Length - 1, 1).Append(ellipsis).ToString();
+ return total.Remove(total.Length - 1, 1).Append(ellipsis);
}
}
}
- return total.ToString();
+ return total;
}
- internal IEnumerable SplitStringSeparate(string text, float width, float scale, Func fontFunction) {
+ internal IEnumerable SplitStringSeparate(CharSource text, float width, float scale, Func fontFunction) {
var currWidth = 0F;
var lastSpaceIndex = -1;
var widthSinceLastSpace = 0F;
@@ -316,7 +276,64 @@ namespace MLEM.Font {
yield return curr.ToString();
}
- private static bool IsTrailingSpace(string s, int index) {
+ private void DrawString(SpriteBatch batch, CharSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
+ var (flipX, flipY) = Vector2.Zero;
+ var flippedV = (effects & SpriteEffects.FlipVertically) != 0;
+ var flippedH = (effects & SpriteEffects.FlipHorizontally) != 0;
+ if (flippedV || flippedH) {
+ var (w, h) = this.MeasureString(text, false, null);
+ if (flippedH) {
+ origin.X *= -1;
+ flipX = -w;
+ }
+ if (flippedV) {
+ origin.Y *= -1;
+ flipY = this.LineHeight - h;
+ }
+ }
+
+ var trans = Matrix.Identity;
+ if (rotation == 0) {
+ trans.M11 = flippedH ? -scale.X : scale.X;
+ trans.M22 = flippedV ? -scale.Y : scale.Y;
+ trans.M41 = (flipX - origin.X) * trans.M11 + position.X;
+ trans.M42 = (flipY - origin.Y) * trans.M22 + position.Y;
+ } else {
+ var sin = (float) Math.Sin(rotation);
+ var cos = (float) Math.Cos(rotation);
+ trans.M11 = (flippedH ? -scale.X : scale.X) * cos;
+ trans.M12 = (flippedH ? -scale.X : scale.X) * sin;
+ trans.M21 = (flippedV ? -scale.Y : scale.Y) * -sin;
+ trans.M22 = (flippedV ? -scale.Y : scale.Y) * cos;
+ trans.M41 = (flipX - origin.X) * trans.M11 + (flipY - origin.Y) * trans.M21 + position.X;
+ trans.M42 = (flipX - origin.X) * trans.M12 + (flipY - origin.Y) * trans.M22 + position.Y;
+ }
+
+ var offset = Vector2.Zero;
+ for (var i = 0; i < text.Length; i++) {
+ var c = text[i];
+ if (c == '\n') {
+ offset.X = 0;
+ offset.Y += this.LineHeight;
+ continue;
+ }
+
+ var cString = c.ToCachedString();
+ var (cW, cH) = this.MeasureString(cString);
+
+ var charPos = offset;
+ if (flippedH)
+ charPos.X += cW;
+ if (flippedV)
+ charPos.Y += cH - this.LineHeight;
+ Vector2.Transform(ref charPos, ref trans, out charPos);
+
+ this.DrawChar(batch, cString, charPos, color, rotation, scale, effects, layerDepth);
+ offset.X += cW;
+ }
+ }
+
+ private static bool IsTrailingSpace(CharSource s, int index) {
for (var i = index + 1; i < s.Length; i++) {
if (s[i] != ' ')
return false;
@@ -324,5 +341,25 @@ namespace MLEM.Font {
return true;
}
+ internal readonly struct CharSource {
+
+ private readonly string strg;
+ private readonly StringBuilder builder;
+
+ public int Length => this.strg?.Length ?? this.builder.Length;
+ public char this[int index] => this.strg?[index] ?? this.builder[index];
+
+ public CharSource(string strg) {
+ this.strg = strg;
+ this.builder = null;
+ }
+
+ public CharSource(StringBuilder builder) {
+ this.strg = null;
+ this.builder = builder;
+ }
+
+ }
+
}
}
\ No newline at end of file
diff --git a/MLEM/Font/GenericSpriteFont.cs b/MLEM/Font/GenericSpriteFont.cs
index 9f29567..64b69aa 100644
--- a/MLEM/Font/GenericSpriteFont.cs
+++ b/MLEM/Font/GenericSpriteFont.cs
@@ -37,8 +37,8 @@ namespace MLEM.Font {
}
///
- protected override void DrawChar(SpriteBatch batch, char c, string cString, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) {
- batch.DrawString(this.Font, cString, position, color, rotation, origin, scale, effects, layerDepth);
+ protected override void DrawChar(SpriteBatch batch, string cString, Vector2 position, Color color, float rotation, Vector2 scale, SpriteEffects effects, float layerDepth) {
+ batch.DrawString(this.Font, cString, position, color, rotation, Vector2.Zero, scale, effects, layerDepth);
}
private static SpriteFont SetDefaults(SpriteFont font) {
diff --git a/MLEM/Formatting/TokenizedString.cs b/MLEM/Formatting/TokenizedString.cs
index 0b2bf88..9dd2cef 100644
--- a/MLEM/Formatting/TokenizedString.cs
+++ b/MLEM/Formatting/TokenizedString.cs
@@ -7,6 +7,7 @@ using MLEM.Extensions;
using MLEM.Font;
using MLEM.Formatting.Codes;
using MLEM.Misc;
+using static MLEM.Font.GenericFont;
namespace MLEM.Formatting {
///
@@ -51,7 +52,6 @@ namespace MLEM.Formatting {
///
/// Splits this tokenized string, inserting newline characters if the width of the string is bigger than the maximum width.
/// Note that a tokenized string can be re-split without losing any of its actual data, as this operation merely modifies the .
- ///
///
/// The font to use for width calculations
/// The maximum width, in display pixels based on the font and scale
@@ -59,7 +59,7 @@ namespace MLEM.Formatting {
/// The text alignment that should be used for width calculations
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 = string.Join("\n", font.SplitStringSeparate(this.String, width, scale, i => this.GetFontForIndex(font, i)));
+ this.modifiedString = string.Join("\n", font.SplitStringSeparate(new CharSource(this.String), width, scale, i => this.GetFontForIndex(font, i)));
this.StoreModifiedSubstrings(font, alignment);
}
@@ -74,13 +74,13 @@ namespace MLEM.Formatting {
/// The characters to add to the end of the string if it is too long
/// The text alignment that should be used for width calculations
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, i => this.GetFontForIndex(font, i));
+ this.modifiedString = font.TruncateString(new CharSource(this.String), width, scale, false, ellipsis, i => this.GetFontForIndex(font, i)).ToString();
this.StoreModifiedSubstrings(font, alignment);
}
///
public Vector2 Measure(GenericFont font) {
- return font.MeasureString(this.DisplayString, false, i => this.GetFontForIndex(font, i));
+ return font.MeasureString(new CharSource(this.DisplayString), false, i => this.GetFontForIndex(font, i));
}
///