mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-22 20:58:34 +01:00
finished the new formatting, finally!
This commit is contained in:
parent
011f9dd4f1
commit
fcd898e16b
10 changed files with 155 additions and 58 deletions
|
@ -8,6 +8,7 @@ using MLEM.Animations;
|
||||||
using MLEM.Extensions;
|
using MLEM.Extensions;
|
||||||
using MLEM.Font;
|
using MLEM.Font;
|
||||||
using MLEM.Formatting;
|
using MLEM.Formatting;
|
||||||
|
using MLEM.Formatting.Codes;
|
||||||
using MLEM.Input;
|
using MLEM.Input;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
using MLEM.Startup;
|
using MLEM.Startup;
|
||||||
|
@ -85,21 +86,12 @@ namespace Demos {
|
||||||
this.root.AddChild(new VerticalSpace(3));
|
this.root.AddChild(new VerticalSpace(3));
|
||||||
|
|
||||||
// a paragraph with formatting codes. To see them all or to add more, check the TextFormatting class
|
// a paragraph with formatting codes. To see them all or to add more, check the TextFormatting class
|
||||||
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Paragraphs can also contain [Blue]formatting codes[White], including colors and [Italic]text styles[Regular]. The names of all [Orange]MonoGame Colors[White] can be used, as well as the codes [Italic]Italic[Regular], [Bold]Bold[Regular], [Shadow]Drop Shadow'd[Regular] and [Shadow][Pink]mixed formatting[Regular][White]. \n[Italic]Even [CornflowerBlue]Cornflower Blue[White] works!"));
|
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Paragraphs can also contain <c Blue>formatting codes</c>, including colors and <i>text styles</i>. The names of all <c Orange>MonoGame Colors</c> can be used, as well as the codes <i>Italic</i>, <b>Bold</b>, <s>Drop Shadow'd</s> and <s><c Pink>mixed formatting</s></c>. \n<i>Even <c #ff611f82>inline custom colors</c> work!</i>"));
|
||||||
|
|
||||||
// adding some custom image formatting codes
|
// adding some custom image formatting codes
|
||||||
TextFormatting.FormattingCodes["Grass"] = new FormattingCode(image.Texture);
|
var p = this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Additionally, you can create custom formatting codes that contain <i Grass> images and more!"));
|
||||||
// formatting codes can also be sprite animations!
|
p.Formatter.AddImage("Grass", image.Texture);
|
||||||
var atlas = new UniformTextureAtlas(LoadContent<Texture2D>("Textures/Anim"), 4, 4);
|
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Defining text animations as formatting codes is also possible, including <a wobbly>wobbly text</a> at <a wobbly 8 0.25>different intensities</a>. Of course, more animations can be added though."));
|
||||||
TextFormatting.FormattingCodes["walk"] = new FormattingCode(new SpriteAnimation(0.2F, atlas[0, 0], atlas[0, 1], atlas[0, 2], atlas[0, 3]));
|
|
||||||
|
|
||||||
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Additionally, you can create custom formatting codes that contain [Grass] images or [Walk] sprite animations!"));
|
|
||||||
|
|
||||||
var animatedPar = this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Defining text animations as formatting codes is also possible, including [Wobbly]wobbly text[Unanimated] as well as a [Typing]dialogue-esque typing effect by default. Of course, more animations can be added though."));
|
|
||||||
this.root.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 10), "Reset Typing Animation") {
|
|
||||||
// to reset any animation, simply change the paragraph's TimeIntoAnimation
|
|
||||||
OnPressed = e => animatedPar.TimeIntoAnimation = TimeSpan.Zero
|
|
||||||
});
|
|
||||||
|
|
||||||
this.root.AddChild(new VerticalSpace(3));
|
this.root.AddChild(new VerticalSpace(3));
|
||||||
this.root.AddChild(new Paragraph(Anchor.AutoCenter, 1, "Text input:", true));
|
this.root.AddChild(new Paragraph(Anchor.AutoCenter, 1, "Text input:", true));
|
||||||
|
|
|
@ -16,11 +16,15 @@ namespace MLEM.Ui.Elements {
|
||||||
|
|
||||||
private string text;
|
private string text;
|
||||||
private string splitText;
|
private string splitText;
|
||||||
|
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||||
public FormattingCodeCollection Formatting;
|
public FormattingCodeCollection Formatting;
|
||||||
public StyleProp<GenericFont> RegularFont;
|
public StyleProp<GenericFont> RegularFont;
|
||||||
public StyleProp<GenericFont> BoldFont;
|
public StyleProp<GenericFont> BoldFont;
|
||||||
public StyleProp<GenericFont> ItalicFont;
|
public StyleProp<GenericFont> ItalicFont;
|
||||||
|
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||||
public StyleProp<FormatSettings> FormatSettings;
|
public StyleProp<FormatSettings> FormatSettings;
|
||||||
|
public readonly TextFormatter Formatter;
|
||||||
|
public TokenizedString TokenizedText { get; private set; }
|
||||||
|
|
||||||
public StyleProp<Color> TextColor;
|
public StyleProp<Color> TextColor;
|
||||||
public StyleProp<float> TextScale;
|
public StyleProp<float> TextScale;
|
||||||
|
@ -36,6 +40,7 @@ namespace MLEM.Ui.Elements {
|
||||||
}
|
}
|
||||||
public bool AutoAdjustWidth;
|
public bool AutoAdjustWidth;
|
||||||
public TextCallback GetTextCallback;
|
public TextCallback GetTextCallback;
|
||||||
|
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||||
public TextModifier RenderedTextModifier = text => text;
|
public TextModifier RenderedTextModifier = text => text;
|
||||||
public TimeSpan TimeIntoAnimation;
|
public TimeSpan TimeIntoAnimation;
|
||||||
|
|
||||||
|
@ -54,19 +59,27 @@ namespace MLEM.Ui.Elements {
|
||||||
this.AutoAdjustWidth = centerText;
|
this.AutoAdjustWidth = centerText;
|
||||||
this.CanBeSelected = false;
|
this.CanBeSelected = false;
|
||||||
this.CanBeMoused = false;
|
this.CanBeMoused = false;
|
||||||
|
this.Formatter = new TextFormatter(() => this.BoldFont, () => this.ItalicFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Vector2 CalcActualSize(RectangleF parentArea) {
|
protected override Vector2 CalcActualSize(RectangleF parentArea) {
|
||||||
var size = base.CalcActualSize(parentArea);
|
var size = base.CalcActualSize(parentArea);
|
||||||
|
|
||||||
var sc = this.TextScale * this.Scale;
|
var sc = this.TextScale * this.Scale;
|
||||||
|
|
||||||
|
// old formatting stuff
|
||||||
this.splitText = this.RegularFont.Value.SplitString(this.text.RemoveFormatting(this.RegularFont.Value), size.X - this.ScaledPadding.Width, sc);
|
this.splitText = this.RegularFont.Value.SplitString(this.text.RemoveFormatting(this.RegularFont.Value), size.X - this.ScaledPadding.Width, sc);
|
||||||
this.Formatting = this.text.GetFormattingCodes(this.RegularFont.Value);
|
this.Formatting = this.text.GetFormattingCodes(this.RegularFont.Value);
|
||||||
|
if (this.Formatting.Count > 0) {
|
||||||
var textDims = this.RegularFont.Value.MeasureString(this.splitText) * sc;
|
var textDims = this.RegularFont.Value.MeasureString(this.splitText) * sc;
|
||||||
return new Vector2(this.AutoAdjustWidth ? textDims.X + this.ScaledPadding.Width : size.X, textDims.Y + this.ScaledPadding.Height);
|
return new Vector2(this.AutoAdjustWidth ? textDims.X + this.ScaledPadding.Width : size.X, textDims.Y + this.ScaledPadding.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.TokenizedText = this.Formatter.Tokenize(this.RegularFont, this.text);
|
||||||
|
this.TokenizedText.Split(this.RegularFont, size.X - this.ScaledPadding.Width, sc);
|
||||||
|
var dims = this.TokenizedText.Measure(this.RegularFont) * sc;
|
||||||
|
return new Vector2(this.AutoAdjustWidth ? dims.X + this.ScaledPadding.Width : size.X, dims.Y + this.ScaledPadding.Height);
|
||||||
|
}
|
||||||
|
|
||||||
public override void ForceUpdateArea() {
|
public override void ForceUpdateArea() {
|
||||||
if (this.GetTextCallback != null)
|
if (this.GetTextCallback != null)
|
||||||
this.Text = this.GetTextCallback(this);
|
this.Text = this.GetTextCallback(this);
|
||||||
|
@ -78,20 +91,20 @@ namespace MLEM.Ui.Elements {
|
||||||
if (this.GetTextCallback != null)
|
if (this.GetTextCallback != null)
|
||||||
this.Text = this.GetTextCallback(this);
|
this.Text = this.GetTextCallback(this);
|
||||||
this.TimeIntoAnimation += time.ElapsedGameTime;
|
this.TimeIntoAnimation += time.ElapsedGameTime;
|
||||||
|
this.TokenizedText?.Update(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) {
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) {
|
||||||
var pos = this.DisplayArea.Location;
|
var pos = this.DisplayArea.Location;
|
||||||
var sc = this.TextScale * this.Scale;
|
var sc = this.TextScale * this.Scale;
|
||||||
|
|
||||||
var toRender = this.RenderedTextModifier(this.splitText);
|
|
||||||
var color = this.TextColor.OrDefault(Color.White) * alpha;
|
var color = this.TextColor.OrDefault(Color.White) * alpha;
|
||||||
// if we don't have any formatting codes, then we don't need to do complex drawing
|
// legacy formatting stuff
|
||||||
if (this.Formatting.Count <= 0) {
|
if (this.Formatting.Count > 0) {
|
||||||
this.RegularFont.Value.DrawString(batch, toRender, pos, color, 0, Vector2.Zero, sc, SpriteEffects.None, 0);
|
var toRender = this.RenderedTextModifier(this.splitText);
|
||||||
} else {
|
|
||||||
// if we have formatting codes, we should do it
|
|
||||||
this.RegularFont.Value.DrawFormattedString(batch, pos, toRender, this.Formatting, color, sc, this.BoldFont.Value, this.ItalicFont.Value, 0, this.TimeIntoAnimation, this.FormatSettings);
|
this.RegularFont.Value.DrawFormattedString(batch, pos, toRender, this.Formatting, color, sc, this.BoldFont.Value, this.ItalicFont.Value, 0, this.TimeIntoAnimation, this.FormatSettings);
|
||||||
|
} else {
|
||||||
|
this.TokenizedText.Draw(time, batch, pos, this.RegularFont, color, sc, 0);
|
||||||
}
|
}
|
||||||
base.Draw(time, batch, alpha, blendState, samplerState, matrix);
|
base.Draw(time, batch, alpha, blendState, samplerState, matrix);
|
||||||
}
|
}
|
||||||
|
@ -107,6 +120,7 @@ namespace MLEM.Ui.Elements {
|
||||||
|
|
||||||
public delegate string TextCallback(Paragraph paragraph);
|
public delegate string TextCallback(Paragraph paragraph);
|
||||||
|
|
||||||
|
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||||
public delegate string TextModifier(string text);
|
public delegate string TextModifier(string text);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Audio;
|
using Microsoft.Xna.Framework.Audio;
|
||||||
using MLEM.Font;
|
using MLEM.Font;
|
||||||
|
@ -38,6 +39,7 @@ namespace MLEM.Ui.Style {
|
||||||
public GenericFont Font;
|
public GenericFont Font;
|
||||||
public GenericFont BoldFont;
|
public GenericFont BoldFont;
|
||||||
public GenericFont ItalicFont;
|
public GenericFont ItalicFont;
|
||||||
|
[Obsolete("Use the new text formatting system in MLEM.Formatting instead")]
|
||||||
public FormatSettings FormatSettings;
|
public FormatSettings FormatSettings;
|
||||||
public float TextScale = 1;
|
public float TextScale = 1;
|
||||||
public SoundEffect ActionSound;
|
public SoundEffect ActionSound;
|
||||||
|
|
|
@ -7,11 +7,13 @@ using MLEM.Misc;
|
||||||
namespace MLEM.Formatting.Codes {
|
namespace MLEM.Formatting.Codes {
|
||||||
public class Code : GenericDataHolder {
|
public class Code : GenericDataHolder {
|
||||||
|
|
||||||
|
public readonly Regex Regex;
|
||||||
public readonly Match Match;
|
public readonly Match Match;
|
||||||
public Token Token { get; internal set; }
|
public Token Token { get; internal set; }
|
||||||
|
|
||||||
public Code(Match match) {
|
protected Code(Match match, Regex regex) {
|
||||||
this.Match = match;
|
this.Match = match;
|
||||||
|
this.Regex = regex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool EndsHere(Code other) {
|
public virtual bool EndsHere(Code other) {
|
||||||
|
@ -29,11 +31,18 @@ namespace MLEM.Formatting.Codes {
|
||||||
public virtual void Update(GameTime time) {
|
public virtual void Update(GameTime time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual string GetReplacementString(GenericFont font) {
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual bool DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
|
public virtual bool DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate Code Constructor(TextFormatter formatter, Match match);
|
public virtual void DrawSelf(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate Code Constructor(TextFormatter formatter, Match match, Regex regex);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ namespace MLEM.Formatting.Codes {
|
||||||
|
|
||||||
private readonly Color? color;
|
private readonly Color? color;
|
||||||
|
|
||||||
public ColorCode(Match match, Color? color) : base(match) {
|
public ColorCode(Match match, Regex regex, Color? color) : base(match, regex) {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace MLEM.Formatting.Codes {
|
||||||
|
|
||||||
private readonly GenericFont font;
|
private readonly GenericFont font;
|
||||||
|
|
||||||
public FontCode(Match match, GenericFont font) : base(match) {
|
public FontCode(Match match, Regex regex, GenericFont font) : base(match, regex) {
|
||||||
this.font = font;
|
this.font = font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
MLEM/Formatting/Codes/ImageCode.cs
Normal file
58
MLEM/Formatting/Codes/ImageCode.cs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
using MLEM.Animations;
|
||||||
|
using MLEM.Extensions;
|
||||||
|
using MLEM.Font;
|
||||||
|
using MLEM.Misc;
|
||||||
|
using MLEM.Textures;
|
||||||
|
|
||||||
|
namespace MLEM.Formatting.Codes {
|
||||||
|
public class ImageCode : Code {
|
||||||
|
|
||||||
|
private readonly SpriteAnimation image;
|
||||||
|
private string replacement;
|
||||||
|
private float gapSize;
|
||||||
|
|
||||||
|
public ImageCode(Match match, Regex regex, SpriteAnimation image) : base(match, regex) {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool EndsHere(Code other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetReplacementString(GenericFont font) {
|
||||||
|
if (this.replacement == null) {
|
||||||
|
// use non-breaking space so that the image won't be line-splitted
|
||||||
|
var strg = font.GetWidthString(font.LineHeight, '\u00A0');
|
||||||
|
this.replacement = strg.Remove(strg.Length - 1) + ' ';
|
||||||
|
this.gapSize = font.MeasureString(this.replacement).X;
|
||||||
|
}
|
||||||
|
return this.replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(GameTime time) {
|
||||||
|
this.image.Update(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawSelf(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
|
||||||
|
var position = pos + new Vector2(this.gapSize - font.LineHeight, 0) / 2 * scale;
|
||||||
|
batch.Draw(this.image.CurrentRegion, new RectangleF(position, new Vector2(font.LineHeight * scale)), Color.White.CopyAlpha(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ImageCodeExtensions {
|
||||||
|
|
||||||
|
public static void AddImage(this TextFormatter formatter, string name, TextureRegion image) {
|
||||||
|
formatter.AddImage(name, new SpriteAnimation(1, image));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddImage(this TextFormatter formatter, string name, SpriteAnimation image) {
|
||||||
|
formatter.Codes.Add(new Regex($"<i {name}>"), (f, m, r) => new ImageCode(m, r, image));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,43 +16,43 @@ namespace MLEM.Formatting {
|
||||||
|
|
||||||
public TextFormatter(Func<GenericFont> boldFont = null, Func<GenericFont> italicFont = null) {
|
public TextFormatter(Func<GenericFont> boldFont = null, Func<GenericFont> italicFont = null) {
|
||||||
// font codes
|
// font codes
|
||||||
this.Codes.Add(new Regex("<b>"), (f, m) => new FontCode(m, boldFont?.Invoke()));
|
this.Codes.Add(new Regex("<b>"), (f, m, r) => new FontCode(m, r, boldFont?.Invoke()));
|
||||||
this.Codes.Add(new Regex("<i>"), (f, m) => new FontCode(m, italicFont?.Invoke()));
|
this.Codes.Add(new Regex("<i>"), (f, m, r) => new FontCode(m, r, italicFont?.Invoke()));
|
||||||
this.Codes.Add(new Regex(@"<s(?: #([0-9\w]{6,8}) (([+-.0-9]*)))?>"), (f, m) => new ShadowCode(m, m.Groups[1].Success ? ColorExtensions.FromHex(m.Groups[1].Value) : Color.Black, new Vector2(float.TryParse(m.Groups[2].Value, out var offset) ? offset : 2)));
|
this.Codes.Add(new Regex(@"<s(?: #([0-9\w]{6,8}) (([+-.0-9]*)))?>"), (f, m, r) => new ShadowCode(m, r, m.Groups[1].Success ? ColorExtensions.FromHex(m.Groups[1].Value) : Color.Black, new Vector2(float.TryParse(m.Groups[2].Value, out var offset) ? offset : 2)));
|
||||||
this.Codes.Add(new Regex("</(b|i|s)>"), (f, m) => new FontCode(m, null));
|
this.Codes.Add(new Regex("</(b|i|s)>"), (f, m, r) => new FontCode(m, r, null));
|
||||||
|
|
||||||
// color codes
|
// color codes
|
||||||
foreach (var c in typeof(Color).GetProperties()) {
|
foreach (var c in typeof(Color).GetProperties()) {
|
||||||
if (c.GetGetMethod().IsStatic) {
|
if (c.GetGetMethod().IsStatic) {
|
||||||
var value = (Color) c.GetValue(null);
|
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 {c.Name}>"), (f, m, r) => new ColorCode(m, r, 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 #([0-9\w]{6,8})>"), (f, m, r) => new ColorCode(m, r, ColorExtensions.FromHex(m.Groups[1].Value)));
|
||||||
this.Codes.Add(new Regex("</c>"), (f, m) => new ColorCode(m, null));
|
this.Codes.Add(new Regex("</c>"), (f, m, r) => new ColorCode(m, r, null));
|
||||||
|
|
||||||
// animation codes
|
// animation codes
|
||||||
this.Codes.Add(new Regex(@"<a wobbly(?: ([+-.0-9]*) ([+-.0-9]*))?>"), (f, m) => new WobblyCode(m, float.TryParse(m.Groups[1].Value, out var mod) ? mod : 5, float.TryParse(m.Groups[2].Value, out var heightMod) ? heightMod : 1 / 8F));
|
this.Codes.Add(new Regex(@"<a wobbly(?: ([+-.0-9]*) ([+-.0-9]*))?>"), (f, m, r) => new WobblyCode(m, r, float.TryParse(m.Groups[1].Value, out var mod) ? mod : 5, float.TryParse(m.Groups[2].Value, out var heightMod) ? heightMod : 1 / 8F));
|
||||||
this.Codes.Add(new Regex("</a>"), (f, m) => new AnimatedCode(m));
|
this.Codes.Add(new Regex("</a>"), (f, m, r) => new AnimatedCode(m, r));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TokenizedString Tokenize(string s) {
|
public TokenizedString Tokenize(GenericFont font, string s) {
|
||||||
var tokens = new List<Token>();
|
var tokens = new List<Token>();
|
||||||
var codes = new List<Code>();
|
var codes = new List<Code>();
|
||||||
var rawIndex = 0;
|
var rawIndex = 0;
|
||||||
while (rawIndex < s.Length) {
|
while (rawIndex < s.Length) {
|
||||||
var index = this.StripFormatting(s.Substring(0, rawIndex)).Length;
|
var index = StripFormatting(font, s.Substring(0, rawIndex), tokens.SelectMany(t => t.AppliedCodes)).Length;
|
||||||
var next = this.GetNextCode(s, rawIndex + 1);
|
var next = this.GetNextCode(s, rawIndex + 1);
|
||||||
// if we've reached the end of the string
|
// if we've reached the end of the string
|
||||||
if (next == null) {
|
if (next == null) {
|
||||||
var sub = s.Substring(rawIndex, s.Length - rawIndex);
|
var sub = s.Substring(rawIndex, s.Length - rawIndex);
|
||||||
tokens.Add(new Token(codes.ToArray(), index, rawIndex, this.StripFormatting(sub), sub));
|
tokens.Add(new Token(codes.ToArray(), index, rawIndex, StripFormatting(font, sub, codes), sub));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new token for the content up to the next code
|
// create a new token for the content up to the next code
|
||||||
var ret = s.Substring(rawIndex, next.Match.Index - rawIndex);
|
var ret = s.Substring(rawIndex, next.Match.Index - rawIndex);
|
||||||
tokens.Add(new Token(codes.ToArray(), index, rawIndex, this.StripFormatting(ret), ret));
|
tokens.Add(new Token(codes.ToArray(), index, rawIndex, StripFormatting(font, ret, codes), ret));
|
||||||
|
|
||||||
// move to the start of the next code
|
// move to the start of the next code
|
||||||
rawIndex = next.Match.Index;
|
rawIndex = next.Match.Index;
|
||||||
|
@ -61,22 +61,22 @@ namespace MLEM.Formatting {
|
||||||
codes.RemoveAll(c => c.EndsHere(next));
|
codes.RemoveAll(c => c.EndsHere(next));
|
||||||
codes.Add(next);
|
codes.Add(next);
|
||||||
}
|
}
|
||||||
return new TokenizedString(s, this.StripFormatting(s), tokens.ToArray());
|
return new TokenizedString(s, StripFormatting(font, s, tokens.SelectMany(t => t.AppliedCodes)), 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) {
|
private Code GetNextCode(string s, int index) {
|
||||||
var (c, m) = this.Codes
|
var (c, m, r) = this.Codes
|
||||||
.Select(kv => (c: kv.Value, m: kv.Key.Match(s, index)))
|
.Select(kv => (c: kv.Value, m: kv.Key.Match(s, index), r: kv.Key))
|
||||||
.Where(kv => kv.m.Success)
|
.Where(kv => kv.m.Success)
|
||||||
.OrderBy(kv => kv.m.Index)
|
.OrderBy(kv => kv.m.Index)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
return c?.Invoke(this, m);
|
return c?.Invoke(this, m, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string StripFormatting(GenericFont font, string s, IEnumerable<Code> codes) {
|
||||||
|
foreach (var code in codes)
|
||||||
|
s = code.Regex.Replace(s, code.GetReplacementString(font));
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace MLEM.Formatting {
|
||||||
public class TokenizedString : GenericDataHolder {
|
public class TokenizedString : GenericDataHolder {
|
||||||
|
|
||||||
public readonly string RawString;
|
public readonly string RawString;
|
||||||
public readonly string String;
|
public string String { get; private set; }
|
||||||
public readonly Token[] Tokens;
|
public readonly Token[] Tokens;
|
||||||
public readonly Code[] AllCodes;
|
public readonly Code[] AllCodes;
|
||||||
|
|
||||||
|
@ -26,18 +26,23 @@ namespace MLEM.Formatting {
|
||||||
public void Split(GenericFont font, float width, float scale) {
|
public void Split(GenericFont font, float width, float scale) {
|
||||||
// a split string has the same character count as the input string
|
// a split string has the same character count as the input string
|
||||||
// but with newline characters added
|
// but with newline characters added
|
||||||
var split = font.SplitString(this.String, width, scale);
|
this.String = font.SplitString(this.String, width, scale);
|
||||||
|
// skip splitting logic for unformatted text
|
||||||
|
if (this.Tokens.Length == 1) {
|
||||||
|
this.Tokens[0].Substring = this.String;
|
||||||
|
return;
|
||||||
|
}
|
||||||
foreach (var token in this.Tokens) {
|
foreach (var token in this.Tokens) {
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var length = 0;
|
var length = 0;
|
||||||
var ret = new StringBuilder();
|
var ret = new StringBuilder();
|
||||||
// this is basically a substring function that ignores newlines for indexing
|
// this is basically a substring function that ignores newlines for indexing
|
||||||
for (var i = 0; i < split.Length; i++) {
|
for (var i = 0; i < this.String.Length; i++) {
|
||||||
// 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 && length < token.Substring.Length)
|
if (index >= token.Index && length < token.Substring.Length)
|
||||||
ret.Append(split[i]);
|
ret.Append(this.String[i]);
|
||||||
// if the current char is not a newline, we simulate length increase
|
// if the current char is not a newline, we simulate length increase
|
||||||
if (split[i] != '\n') {
|
if (this.String[i] != '\n') {
|
||||||
if (index >= token.Index)
|
if (index >= token.Index)
|
||||||
length++;
|
length++;
|
||||||
index++;
|
index++;
|
||||||
|
@ -47,6 +52,10 @@ namespace MLEM.Formatting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector2 Measure(GenericFont font) {
|
||||||
|
return font.MeasureString(this.String);
|
||||||
|
}
|
||||||
|
|
||||||
public void Update(GameTime time) {
|
public void Update(GameTime time) {
|
||||||
foreach (var code in this.AllCodes)
|
foreach (var code in this.AllCodes)
|
||||||
code.Update(time);
|
code.Update(time);
|
||||||
|
@ -62,8 +71,9 @@ namespace MLEM.Formatting {
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
innerOffset.X = 0;
|
innerOffset.X = 0;
|
||||||
innerOffset.Y += font.LineHeight * scale;
|
innerOffset.Y += font.LineHeight * scale;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
if (i == 0)
|
||||||
|
token.DrawSelf(time, batch, pos + innerOffset, font, color, scale, depth);
|
||||||
|
|
||||||
var cString = c.ToString();
|
var cString = c.ToString();
|
||||||
token.DrawCharacter(time, batch, c, cString, i, pos + innerOffset, drawFont, drawColor, scale, depth);
|
token.DrawCharacter(time, batch, c, cString, i, pos + innerOffset, drawFont, drawColor, scale, depth);
|
||||||
|
|
|
@ -12,6 +12,8 @@ using MLEM.Extended.Tiled;
|
||||||
using MLEM.Extensions;
|
using MLEM.Extensions;
|
||||||
using MLEM.Font;
|
using MLEM.Font;
|
||||||
using MLEM.Formatting;
|
using MLEM.Formatting;
|
||||||
|
using MLEM.Formatting.Codes;
|
||||||
|
using MLEM.Input;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
using MLEM.Startup;
|
using MLEM.Startup;
|
||||||
using MLEM.Textures;
|
using MLEM.Textures;
|
||||||
|
@ -113,16 +115,26 @@ namespace Sandbox {
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
var formatter = new TextFormatter();
|
var formatter = new TextFormatter();
|
||||||
var strg = "This <c CornflowerBlue>is a formatted string</c> with <c #ff0000>two bits of formatting</c>! It also includesavery<c Pink>long</c>wordthatis<c Blue>formatted</c>aswell. Additionally, it <a wobbly>wobbles</a> and has a <s>shadow</s> or a <s #ff0000 4>weird shadow</s>";
|
formatter.AddImage("Test", new TextureRegion(tex, 0, 8, 24, 24));
|
||||||
this.tokenized = formatter.Tokenize(strg);
|
//var strg = "This <c CornflowerBlue>is a formatted string</c> with <c #ffff0000>two bits of formatting</c>! It also includesavery<c Pink>long</c>wordthatis<c Blue>formatted</c>aswell. Additionally, it <a wobbly>wobbles</a> and has a <s>shadow</s> or a <s #ffff0000 4>weird shadow</s>. We like icons too! <i Test> <i Test> <i Test> <i Test> <i Test> <i Test> <i Test> <i Test> <i Test> <i Test> <i Test> <i Test> <i Test><i Test><i Test><i Test><i Test><i Test><i Test><i Test><i Test><i Test><i Test><i Test><i Test>";
|
||||||
|
var 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. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
|
||||||
|
//var strg = "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. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
|
||||||
|
this.tokenized = formatter.Tokenize(font, strg);
|
||||||
this.tokenized.Split(font, 400, 1);
|
this.tokenized.Split(font, 400, 1);
|
||||||
|
|
||||||
this.OnDraw += (g, time) => {
|
this.OnDraw += (g, time) => {
|
||||||
this.SpriteBatch.Begin();
|
this.SpriteBatch.Begin();
|
||||||
|
this.SpriteBatch.FillRectangle(new RectangleF(400, 20, 400, 1000), Color.Green);
|
||||||
this.tokenized.Draw(time, this.SpriteBatch, new Vector2(400, 20), font, Color.White, 1, 0);
|
this.tokenized.Draw(time, this.SpriteBatch, new Vector2(400, 20), font, Color.White, 1, 0);
|
||||||
this.SpriteBatch.End();
|
this.SpriteBatch.End();
|
||||||
};
|
};
|
||||||
this.OnUpdate += (g, time) => this.tokenized.Update(time);
|
this.OnUpdate += (g, time) => {
|
||||||
|
if (this.InputHandler.IsPressed(Keys.W)) {
|
||||||
|
this.tokenized = formatter.Tokenize(font, strg);
|
||||||
|
this.tokenized.Split(font, this.InputHandler.IsModifierKeyDown(ModifierKey.Shift) ? 400 : 500, 1);
|
||||||
|
}
|
||||||
|
this.tokenized.Update(time);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void DoUpdate(GameTime gameTime) {
|
protected override void DoUpdate(GameTime gameTime) {
|
||||||
|
|
Loading…
Reference in a new issue