1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-22 12:58:33 +01:00

simplify text rendering quite a bit after realizing that monogame deals with multiple lines on its own

This commit is contained in:
Ellpeck 2019-08-25 19:07:45 +02:00
parent 6766d769f4
commit 226640ef3a
8 changed files with 64 additions and 84 deletions

View file

@ -69,7 +69,7 @@ namespace Demos {
var root = new Panel(Anchor.Center, new Vector2(80, 100), Vector2.Zero, false, true, new Point(5, 10)); var root = new Panel(Anchor.Center, new Vector2(80, 100), Vector2.Zero, false, true, new Point(5, 10));
this.UiSystem.Add("Test", root); this.UiSystem.Add("Test", root);
root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "This is a small demo for MLEM.Ui, a user interface library that is part of (M)LEM (L)ibrary by (E)llpeck for (M)onoGame.") {LineSpace = 1.5F}); root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "This is a small demo for MLEM.Ui, a user interface library that is part of (M)LEM (L)ibrary by (E)llpeck for (M)onoGame."));
var image = root.AddChild(new Image(Anchor.AutoCenter, new Vector2(50, 50), new TextureRegion(this.testTexture, 0, 0, 8, 8)) {IsHidden = true, Padding = new Point(3)}); var image = root.AddChild(new Image(Anchor.AutoCenter, new Vector2(50, 50), new TextureRegion(this.testTexture, 0, 0, 8, 8)) {IsHidden = true, Padding = new Point(3)});
// Setting the x or y coordinate of the size to 1 or a lower number causes the width or height to be a percentage of the parent's width or height // Setting the x or y coordinate of the size to 1 or a lower number causes the width or height to be a percentage of the parent's width or height
// (for example, setting the size's x to 0.75 would make the element's width be 0.75*parentWidth) // (for example, setting the size's x to 0.75 would make the element's width be 0.75*parentWidth)

View file

@ -1,25 +1,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
using MonoGame.Extended.BitmapFonts; using MonoGame.Extended.BitmapFonts;
namespace MLEM.Extended.Extensions { namespace MLEM.Extended.Extensions {
public static class BitmapFontExtensions { public static class BitmapFontExtensions {
public static IEnumerable<string> SplitString(this BitmapFont font, string text, float width, float scale) { public static string SplitString(this BitmapFont font, string text, float width, float scale) {
var builder = new StringBuilder(); return SpriteFontExtensions.SplitString(s => font.MeasureString(s).Width, text, width, scale);
foreach (var line in text.Split('\n')) {
foreach (var word in line.Split(' ')) {
builder.Append(word).Append(' ');
if (font.MeasureString(builder).Width * scale >= width) {
var len = builder.Length - word.Length - 1;
yield return builder.ToString(0, len - 1);
builder.Remove(0, len);
}
}
yield return builder.ToString(0, builder.Length - 1);
builder.Clear();
}
} }
} }

View file

@ -10,6 +10,7 @@ namespace MLEM.Extended.Font {
public class GenericBitmapFont : IGenericFont { public class GenericBitmapFont : IGenericFont {
public readonly BitmapFont Font; public readonly BitmapFont Font;
public float LineHeight => this.Font.LineHeight;
public GenericBitmapFont(BitmapFont font) { public GenericBitmapFont(BitmapFont font) {
this.Font = font; this.Font = font;
@ -55,7 +56,7 @@ namespace MLEM.Extended.Font {
batch.DrawCenteredString(this.Font, text, position, scale, color, horizontal, vertical, addedScale); batch.DrawCenteredString(this.Font, text, position, scale, color, horizontal, vertical, addedScale);
} }
public IEnumerable<string> SplitString(string text, float width, float scale) { public string SplitString(string text, float width, float scale) {
return this.Font.SplitString(text, width, scale); return this.Font.SplitString(text, width, scale);
} }

View file

@ -14,9 +14,7 @@ namespace MLEM.Ui.Elements {
public class Paragraph : Element { public class Paragraph : Element {
private string text; private string text;
private float lineHeight; private string splitText;
private float longestLineLength;
private string[] splitText;
private Dictionary<int, FormattingCode> codeLocations; private Dictionary<int, FormattingCode> codeLocations;
private IGenericFont regularFont; private IGenericFont regularFont;
private IGenericFont boldFont; private IGenericFont boldFont;
@ -37,7 +35,6 @@ namespace MLEM.Ui.Elements {
} }
public bool AutoAdjustWidth; public bool AutoAdjustWidth;
public TextCallback GetTextCallback; public TextCallback GetTextCallback;
public float LineSpace = 1;
public Paragraph(Anchor anchor, float width, TextCallback textCallback, bool centerText = false) public Paragraph(Anchor anchor, float width, TextCallback textCallback, bool centerText = false)
: this(anchor, width, "", centerText) { : this(anchor, width, "", centerText) {
@ -53,21 +50,13 @@ namespace MLEM.Ui.Elements {
protected override Point CalcActualSize(Rectangle parentArea) { protected override Point CalcActualSize(Rectangle parentArea) {
var size = base.CalcActualSize(parentArea); var size = base.CalcActualSize(parentArea);
var sc = this.TextScale * this.Scale; var sc = this.TextScale * this.Scale;
this.splitText = this.regularFont.SplitString(this.text.RemoveFormatting(), size.X - this.ScaledPadding.X * 2, sc).ToArray(); this.splitText = this.regularFont.SplitString(this.text.RemoveFormatting(), size.X - this.ScaledPadding.X * 2, sc);
this.codeLocations = this.text.GetFormattingCodes(); this.codeLocations = this.text.GetFormattingCodes();
this.lineHeight = 0; var textDims = this.regularFont.MeasureString(this.splitText) * sc;
this.longestLineLength = 0; return new Point(this.AutoAdjustWidth ? textDims.X.Ceil() + this.ScaledPadding.X * 2 : size.X, textDims.Y.Ceil() + this.ScaledPadding.Y * 2);
foreach (var strg in this.splitText) {
var strgScale = this.regularFont.MeasureString(strg) * sc;
if (strgScale.Y + 1 > this.lineHeight)
this.lineHeight = strgScale.Y + 1;
if (strgScale.X > this.longestLineLength)
this.longestLineLength = strgScale.X;
}
this.lineHeight *= this.LineSpace;
return new Point(this.AutoAdjustWidth ? this.longestLineLength.Ceil() + this.ScaledPadding.X * 2 : size.X, (this.lineHeight * this.splitText.Length).Ceil() + this.ScaledPadding.Y * 2);
} }
public override void Update(GameTime time) { public override void Update(GameTime time) {
@ -86,51 +75,45 @@ namespace MLEM.Ui.Elements {
// if we don't have any formatting codes, then we don't need to do complex drawing // if we don't have any formatting codes, then we don't need to do complex drawing
if (this.codeLocations.Count <= 0) { if (this.codeLocations.Count <= 0) {
foreach (var line in this.splitText) { this.regularFont.DrawString(batch, this.splitText, pos + off, this.TextColor * alpha, 0, Vector2.Zero, sc, SpriteEffects.None, 0);
this.regularFont.DrawString(batch, line, pos + off, this.TextColor * alpha, 0, Vector2.Zero, sc, SpriteEffects.None, 0);
off.Y += this.lineHeight;
}
} else { } else {
// if we have formatting codes, we need to go through each index and see how it should be drawn // if we have formatting codes, we need to go through each index and see how it should be drawn
var characterCounter = 0; var characterCounter = 0;
var currColor = this.TextColor; var currColor = this.TextColor;
var currFont = this.regularFont; var currFont = this.regularFont;
foreach (var line in this.splitText) { var innerOffset = new Vector2();
var lineOffset = new Vector2(); foreach (var c in this.splitText) {
foreach (var c in line) { // check if the current character's index has a formatting code
// check if the current character's index has a formatting code this.codeLocations.TryGetValue(characterCounter, out var code);
this.codeLocations.TryGetValue(characterCounter, out var code); if (code != null) {
if (code != null) { // if so, apply it
// if so, apply it if (code.IsColorCode) {
if (code.IsColorCode) { currColor = code.Color;
currColor = code.Color; } else {
} else { switch (code.Style) {
switch (code.Style) { case TextStyle.Regular:
case TextStyle.Regular: currFont = this.regularFont;
currFont = this.regularFont; break;
break; case TextStyle.Bold:
case TextStyle.Bold: currFont = this.boldFont;
currFont = this.boldFont; break;
break; case TextStyle.Italic:
case TextStyle.Italic: currFont = this.italicFont;
currFont = this.italicFont; break;
break;
}
} }
} }
var cSt = c.ToString();
currFont.DrawString(batch, cSt, pos + off + lineOffset, currColor * alpha, 0, Vector2.Zero, sc, SpriteEffects.None, 0);
// get the width based on the regular font so that the split text doesn't overshoot the borders
// this is a bit of a hack, but bold fonts shouldn't be that much thicker so it won't look bad
lineOffset.X += this.regularFont.MeasureString(cSt).X * sc;
characterCounter++;
} }
// spaces are replaced by newline characters, account for that
characterCounter++; characterCounter++;
off.Y += this.lineHeight;
var cSt = c.ToString();
if (c == '\n') {
innerOffset.X = 0;
innerOffset.Y += this.regularFont.LineHeight * sc;
} else {
currFont.DrawString(batch, cSt, pos + off + innerOffset, currColor * alpha, 0, Vector2.Zero, sc, SpriteEffects.None, 0);
innerOffset.X += this.regularFont.MeasureString(cSt).X * sc;
}
} }
} }
base.Draw(time, batch, alpha, offset); base.Draw(time, batch, alpha, offset);

View file

@ -45,6 +45,8 @@ namespace MLEM.Ui.Style {
private class EmptyFont : IGenericFont { private class EmptyFont : IGenericFont {
public float LineHeight => 1;
public Vector2 MeasureString(string text) { public Vector2 MeasureString(string text) {
return Vector2.One; return Vector2.One;
} }
@ -74,8 +76,8 @@ namespace MLEM.Ui.Style {
public void DrawCenteredString(SpriteBatch batch, string text, Vector2 position, float scale, Color color, bool horizontal = true, bool vertical = false, float addedScale = 0) { public void DrawCenteredString(SpriteBatch batch, string text, Vector2 position, float scale, Color color, bool horizontal = true, bool vertical = false, float addedScale = 0) {
} }
public IEnumerable<string> SplitString(string text, float width, float scale) { public string SplitString(string text, float width, float scale) {
yield break; return text;
} }
} }

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@ -5,20 +6,25 @@ using Microsoft.Xna.Framework.Graphics;
namespace MLEM.Extensions { namespace MLEM.Extensions {
public static class SpriteFontExtensions { public static class SpriteFontExtensions {
public static IEnumerable<string> SplitString(this SpriteFont font, string text, float width, float scale) { public static string SplitString(this SpriteFont font, string text, float width, float scale) {
var builder = new StringBuilder(); return SplitString(s => font.MeasureString(s).X, text, width, scale);
}
public static string SplitString(Func<StringBuilder, float> widthFunc, string text, float width, float scale) {
var total = new StringBuilder();
foreach (var line in text.Split('\n')) { foreach (var line in text.Split('\n')) {
var curr = new StringBuilder();
foreach (var word in line.Split(' ')) { foreach (var word in line.Split(' ')) {
builder.Append(word).Append(' '); curr.Append(word).Append(' ');
if (font.MeasureString(builder).X * scale >= width) { if (widthFunc(curr) * scale >= width) {
var len = builder.Length - word.Length - 1; var len = curr.Length - word.Length - 1;
yield return builder.ToString(0, len - 1); total.Append(curr.ToString(0, len - 1)).Append('\n');
builder.Remove(0, len); curr.Remove(0, len);
} }
} }
yield return builder.ToString(0, builder.Length - 1); total.Append(curr.ToString(0, curr.Length - 1)).Append('\n');
builder.Clear();
} }
return total.ToString(0, total.Length - 1);
} }
} }

View file

@ -8,15 +8,12 @@ namespace MLEM.Font {
public class GenericSpriteFont : IGenericFont { public class GenericSpriteFont : IGenericFont {
public readonly SpriteFont Font; public readonly SpriteFont Font;
public float LineHeight => this.Font.LineSpacing;
public GenericSpriteFont(SpriteFont font) { public GenericSpriteFont(SpriteFont font) {
this.Font = font; this.Font = font;
} }
public static implicit operator GenericSpriteFont(SpriteFont font) {
return new GenericSpriteFont(font);
}
public Vector2 MeasureString(string text) { public Vector2 MeasureString(string text) {
return this.Font.MeasureString(text); return this.Font.MeasureString(text);
} }
@ -53,7 +50,7 @@ namespace MLEM.Font {
batch.DrawCenteredString(this.Font, text, position, scale, color, horizontal, vertical, addedScale); batch.DrawCenteredString(this.Font, text, position, scale, color, horizontal, vertical, addedScale);
} }
public IEnumerable<string> SplitString(string text, float width, float scale) { public string SplitString(string text, float width, float scale) {
return this.Font.SplitString(text, width, scale); return this.Font.SplitString(text, width, scale);
} }

View file

@ -6,6 +6,8 @@ using Microsoft.Xna.Framework.Graphics;
namespace MLEM.Font { namespace MLEM.Font {
public interface IGenericFont { public interface IGenericFont {
float LineHeight { get; }
Vector2 MeasureString(string text); Vector2 MeasureString(string text);
Vector2 MeasureString(StringBuilder text); Vector2 MeasureString(StringBuilder text);
@ -24,7 +26,7 @@ namespace MLEM.Font {
void DrawCenteredString(SpriteBatch batch, string text, Vector2 position, float scale, Color color, bool horizontal = true, bool vertical = false, float addedScale = 0); void DrawCenteredString(SpriteBatch batch, string text, Vector2 position, float scale, Color color, bool horizontal = true, bool vertical = false, float addedScale = 0);
IEnumerable<string> SplitString(string text, float width, float scale); string SplitString(string text, float width, float scale);
} }
} }