mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-12-25 17:59:24 +01:00
new text formatting, part 1
This commit is contained in:
parent
f72962808b
commit
8398499edd
12 changed files with 272 additions and 6 deletions
35
MLEM/Formatting/Codes/Code.cs
Normal file
35
MLEM/Formatting/Codes/Code.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MLEM.Font;
|
||||
|
||||
namespace MLEM.Formatting.Codes {
|
||||
public class Code {
|
||||
|
||||
public readonly Match Match;
|
||||
public Token Token { get; internal set; }
|
||||
|
||||
public Code(Match match) {
|
||||
this.Match = match;
|
||||
}
|
||||
|
||||
public virtual bool EndsHere(Code other) {
|
||||
return other.GetType() == this.GetType();
|
||||
}
|
||||
|
||||
public virtual Color? GetColor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual GenericFont GetFont() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual bool DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public delegate Code Constructor(TextFormatter formatter, Match match);
|
||||
|
||||
}
|
||||
}
|
18
MLEM/Formatting/Codes/ColorCode.cs
Normal file
18
MLEM/Formatting/Codes/ColorCode.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MLEM.Formatting.Codes {
|
||||
public class ColorCode : Code {
|
||||
|
||||
private readonly Color? color;
|
||||
|
||||
public ColorCode(Match match, Color? color) : base(match) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public override Color? GetColor() {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
18
MLEM/Formatting/Codes/FontCode.cs
Normal file
18
MLEM/Formatting/Codes/FontCode.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using MLEM.Font;
|
||||
|
||||
namespace MLEM.Formatting.Codes {
|
||||
public class FontCode : Code {
|
||||
|
||||
private readonly GenericFont font;
|
||||
|
||||
public FontCode(Match match, GenericFont font) : base(match) {
|
||||
this.font = font;
|
||||
}
|
||||
|
||||
public override GenericFont GetFont() {
|
||||
return this.font;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MLEM.Misc;
|
||||
|
||||
namespace MLEM.Formatting {
|
||||
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||
public class FormatSettings : GenericDataHolder {
|
||||
|
||||
public static readonly FormatSettings Default = new FormatSettings();
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MLEM.Animations;
|
||||
using MLEM.Extensions;
|
||||
|
@ -5,6 +6,7 @@ using MLEM.Font;
|
|||
using MLEM.Textures;
|
||||
|
||||
namespace MLEM.Formatting {
|
||||
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||
public class FormattingCode {
|
||||
|
||||
public readonly Type CodeType;
|
|
@ -1,12 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using MLEM.Misc;
|
||||
|
||||
namespace MLEM.Formatting {
|
||||
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||
public class FormattingCodeCollection : Dictionary<int, List<FormattingCodeData>> {
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||
public class FormattingCodeData : GenericDataHolder {
|
||||
|
||||
public readonly FormattingCode Code;
|
|
@ -4,6 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
|
|||
using MLEM.Font;
|
||||
|
||||
namespace MLEM.Formatting {
|
||||
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||
public static class TextAnimation {
|
||||
|
||||
public static readonly DrawCharacter Default = (settings, font, batch, totalText, index, effectStartIndex, charSt, position, color, scale, layerDepth, timeIntoAnimation) => {
|
|
@ -9,6 +9,7 @@ using MLEM.Misc;
|
|||
using MLEM.Textures;
|
||||
|
||||
namespace MLEM.Formatting {
|
||||
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||
public static class TextFormatting {
|
||||
|
||||
public static readonly Dictionary<string, FormattingCode> FormattingCodes = new Dictionary<string, FormattingCode>(StringComparer.OrdinalIgnoreCase);
|
77
MLEM/Formatting/TextFormatter.cs
Normal file
77
MLEM/Formatting/TextFormatter.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MLEM.Extensions;
|
||||
using MLEM.Font;
|
||||
using MLEM.Formatting.Codes;
|
||||
|
||||
namespace MLEM.Formatting {
|
||||
public class TextFormatter {
|
||||
|
||||
public readonly Dictionary<Regex, Code.Constructor> Codes = new Dictionary<Regex, Code.Constructor>();
|
||||
|
||||
public TextFormatter(Func<GenericFont> boldFont = null, Func<GenericFont> italicFont = null) {
|
||||
// font codes
|
||||
this.Codes.Add(new Regex("<b>"), (f, m) => new FontCode(m, boldFont?.Invoke()));
|
||||
this.Codes.Add(new Regex("<i>"), (f, m) => new FontCode(m, italicFont?.Invoke()));
|
||||
this.Codes.Add(new Regex("</(b|i)>"), (f, m) => new FontCode(m, null));
|
||||
|
||||
// color codes
|
||||
foreach (var c in typeof(Color).GetProperties()) {
|
||||
if (c.GetGetMethod().IsStatic) {
|
||||
var value = (Color) c.GetValue(null);
|
||||
this.Codes.Add(new Regex($"<c {c.Name}>"), (f, m) => new ColorCode(m, value));
|
||||
}
|
||||
}
|
||||
this.Codes.Add(new Regex(@"<c #([0-9\w]{6,8})>"), (f, m) => new ColorCode(m, ColorExtensions.FromHex(m.Groups[1].Value)));
|
||||
this.Codes.Add(new Regex("</c>"), (f, m) => new ColorCode(m, null));
|
||||
}
|
||||
|
||||
public TokenizedString Tokenize(string s) {
|
||||
var tokens = new List<Token>();
|
||||
var codes = new List<Code>();
|
||||
var rawIndex = 0;
|
||||
while (rawIndex < s.Length) {
|
||||
var index = this.StripFormatting(s.Substring(0, rawIndex)).Length;
|
||||
var next = this.GetNextCode(s, rawIndex + 1);
|
||||
// if we've reached the end of the string
|
||||
if (next == null) {
|
||||
var sub = s.Substring(rawIndex, s.Length - rawIndex);
|
||||
tokens.Add(new Token(codes.ToArray(), index, rawIndex, this.StripFormatting(sub), sub));
|
||||
break;
|
||||
}
|
||||
|
||||
// create a new token for the content up to the next code
|
||||
var ret = s.Substring(rawIndex, next.Match.Index - rawIndex);
|
||||
tokens.Add(new Token(codes.ToArray(), index, rawIndex, this.StripFormatting(ret), ret));
|
||||
|
||||
// move to the start of the next code
|
||||
rawIndex = next.Match.Index;
|
||||
|
||||
// remove all codes that are incompatible with the next one and apply it
|
||||
codes.RemoveAll(c => c.EndsHere(next));
|
||||
codes.Add(next);
|
||||
}
|
||||
return new TokenizedString(s, this.StripFormatting(s), tokens.ToArray());
|
||||
}
|
||||
|
||||
public string StripFormatting(string s) {
|
||||
foreach (var regex in this.Codes.Keys)
|
||||
s = regex.Replace(s, string.Empty);
|
||||
return s;
|
||||
}
|
||||
|
||||
private Code GetNextCode(string s, int index) {
|
||||
var (c, m) = this.Codes
|
||||
.Select(kv => (c: kv.Value, m: kv.Key.Match(s, index)))
|
||||
.Where(kv => kv.m.Success)
|
||||
.OrderBy(kv => kv.m.Index)
|
||||
.FirstOrDefault();
|
||||
return c?.Invoke(this, m);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
47
MLEM/Formatting/Token.cs
Normal file
47
MLEM/Formatting/Token.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MLEM.Font;
|
||||
using MLEM.Formatting.Codes;
|
||||
|
||||
namespace MLEM.Formatting {
|
||||
public class Token {
|
||||
|
||||
public readonly Code[] AppliedCodes;
|
||||
public readonly int Index;
|
||||
public readonly int RawIndex;
|
||||
public string Substring { get; internal set; }
|
||||
public readonly string RawSubstring;
|
||||
|
||||
public Token(Code[] appliedCodes, int index, int rawIndex, string substring, string rawSubstring) {
|
||||
this.AppliedCodes = appliedCodes;
|
||||
this.Index = index;
|
||||
this.RawIndex = rawIndex;
|
||||
this.Substring = substring;
|
||||
this.RawSubstring = rawSubstring;
|
||||
|
||||
foreach (var code in appliedCodes)
|
||||
code.Token = this;
|
||||
}
|
||||
|
||||
public Color? GetColor() {
|
||||
return this.AppliedCodes.Select(c => c.GetColor()).FirstOrDefault(c => c.HasValue);
|
||||
}
|
||||
|
||||
public GenericFont GetFont() {
|
||||
return this.AppliedCodes.Select(c => c.GetFont()).FirstOrDefault();
|
||||
}
|
||||
|
||||
public void DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
|
||||
foreach (var code in this.AppliedCodes) {
|
||||
if (code.DrawCharacter(time, batch, c, cString, pos, font, color, scale, depth))
|
||||
return;
|
||||
}
|
||||
|
||||
// if no code drew, we have to do it ourselves
|
||||
font.DrawString(batch, cString, pos, color, 0, Vector2.Zero, scale, SpriteEffects.None, depth);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
48
MLEM/Formatting/TokenizedString.cs
Normal file
48
MLEM/Formatting/TokenizedString.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MLEM.Font;
|
||||
|
||||
namespace MLEM.Formatting {
|
||||
public struct TokenizedString {
|
||||
|
||||
public readonly string RawString;
|
||||
public readonly string String;
|
||||
public readonly Token[] Tokens;
|
||||
|
||||
public TokenizedString(string rawString, string strg, Token[] tokens) {
|
||||
this.RawString = rawString;
|
||||
this.String = strg;
|
||||
this.Tokens = tokens;
|
||||
}
|
||||
|
||||
public void Split(GenericFont font, float width, float scale) {
|
||||
var split = font.SplitString(this.String, width, scale);
|
||||
// remove spaces at the end of new lines since we want the same character count
|
||||
split = split.Replace(" \n", "\n");
|
||||
foreach (var token in this.Tokens)
|
||||
token.Substring = split.Substring(token.Index, token.Substring.Length);
|
||||
}
|
||||
|
||||
public void Draw(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
|
||||
var innerOffset = new Vector2();
|
||||
foreach (var token in this.Tokens) {
|
||||
var drawFont = token.GetFont() ?? font;
|
||||
var drawColor = token.GetColor() ?? color;
|
||||
foreach (var c in token.Substring) {
|
||||
if (c == '\n') {
|
||||
innerOffset.X = 0;
|
||||
innerOffset.Y += font.LineHeight * scale;
|
||||
continue;
|
||||
}
|
||||
|
||||
var cString = c.ToString();
|
||||
token.DrawCharacter(time, batch, c, cString, pos + innerOffset, drawFont, drawColor, scale, depth);
|
||||
innerOffset.X += font.MeasureString(cString).X * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
@ -10,6 +11,7 @@ using MLEM.Extended.Extensions;
|
|||
using MLEM.Extended.Tiled;
|
||||
using MLEM.Extensions;
|
||||
using MLEM.Font;
|
||||
using MLEM.Formatting;
|
||||
using MLEM.Misc;
|
||||
using MLEM.Startup;
|
||||
using MLEM.Textures;
|
||||
|
@ -29,6 +31,7 @@ namespace Sandbox {
|
|||
private IndividualTiledMapRenderer mapRenderer;
|
||||
private TiledMapCollisions collisions;
|
||||
private RawContentManager rawContent;
|
||||
private TokenizedString tokenized;
|
||||
|
||||
public GameImpl() {
|
||||
this.IsMouseVisible = true;
|
||||
|
@ -72,7 +75,7 @@ namespace Sandbox {
|
|||
this.UiSystem.Add("Panel", panel);
|
||||
|
||||
panel.SetData("TestKey", new Vector2(10, 2));
|
||||
Console.WriteLine(panel.GetData<Vector2>("TestKey"));
|
||||
//Console.WriteLine(panel.GetData<Vector2>("TestKey"));
|
||||
|
||||
var obj = new Test {
|
||||
Vec = new Vector2(10, 20),
|
||||
|
@ -84,16 +87,16 @@ namespace Sandbox {
|
|||
|
||||
var writer = new StringWriter();
|
||||
this.Content.GetJsonSerializer().Serialize(writer, obj);
|
||||
Console.WriteLine(writer.ToString());
|
||||
//Console.WriteLine(writer.ToString());
|
||||
// {"Vec":"10 20","Point":"20 30","Rectangle":"1 2 3 4","RectangleF":"4 5 6 7"}
|
||||
|
||||
// Also:
|
||||
//this.Content.AddJsonConverter(new CustomConverter());
|
||||
|
||||
var res = this.Content.LoadJson<Test>("Test");
|
||||
Console.WriteLine(res);
|
||||
//Console.WriteLine(res);
|
||||
|
||||
this.OnDraw += (game, time) => {
|
||||
/*this.OnDraw += (game, time) => {
|
||||
this.SpriteBatch.Begin();
|
||||
font.DrawString(this.SpriteBatch, "Left Aligned\nover multiple lines", new Vector2(640, 0), TextAlign.Left, Color.White);
|
||||
font.DrawString(this.SpriteBatch, "Center Aligned\nover multiple lines", new Vector2(640, 100), TextAlign.Center, Color.White);
|
||||
|
@ -107,6 +110,17 @@ namespace Sandbox {
|
|||
font.DrawString(this.SpriteBatch, font.TruncateString("This is a very long string", 200, 1, true), new Vector2(200, 500), Color.White);
|
||||
font.DrawString(this.SpriteBatch, font.TruncateString("This is a very long string", 200, 1, true, "..."), new Vector2(200, 550), Color.White);
|
||||
this.SpriteBatch.End();
|
||||
};*/
|
||||
|
||||
var formatter = new TextFormatter();
|
||||
var strg = "This <c CornflowerBlue>is a formatted string</c> with <c #ff0000>two bits of formatting</c>!";
|
||||
this.tokenized = formatter.Tokenize(strg);
|
||||
this.tokenized.Split(font, 400, 1);
|
||||
|
||||
this.OnDraw += (g, time) => {
|
||||
this.SpriteBatch.Begin();
|
||||
this.tokenized.Draw(time, this.SpriteBatch, new Vector2(100, 20), font, Color.White, 1, 0);
|
||||
this.SpriteBatch.End();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -124,13 +138,13 @@ namespace Sandbox {
|
|||
protected override void DoDraw(GameTime gameTime) {
|
||||
this.GraphicsDevice.Clear(Color.Black);
|
||||
this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, this.camera.ViewMatrix);
|
||||
this.mapRenderer.Draw(this.SpriteBatch, this.camera.GetVisibleRectangle().ToExtended());
|
||||
/*this.mapRenderer.Draw(this.SpriteBatch, this.camera.GetVisibleRectangle().ToExtended());
|
||||
|
||||
foreach (var tile in this.collisions.GetCollidingTiles(new RectangleF(0, 0, this.map.Width, this.map.Height))) {
|
||||
foreach (var area in tile.Collisions) {
|
||||
this.SpriteBatch.DrawRectangle(area.Position * this.map.GetTileSize(), area.Size * this.map.GetTileSize(), Color.Red);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
this.SpriteBatch.End();
|
||||
base.DoDraw(gameTime);
|
||||
|
|
Loading…
Reference in a new issue