From b480026b7ecb11a2b06c177107ee900dd9845b33 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 3 Feb 2020 03:04:13 +0100 Subject: [PATCH] formatting code redesign, part 1: Regex! --- Demos/UiDemo.cs | 10 ++--- MLEM.Ui/Elements/Paragraph.cs | 12 +++--- MLEM/Animations/SpriteAnimation.cs | 6 +-- MLEM/Formatting/AnimationCode.cs | 40 +++++++++++++++++++ MLEM/Formatting/FormattingCode.cs | 31 ++++++++------- MLEM/Formatting/FormattingCodeCollection.cs | 18 +++++++++ MLEM/Formatting/TextAnimation.cs | 26 ------------- MLEM/Formatting/TextFormatting.cs | 43 +++++++++++---------- 8 files changed, 111 insertions(+), 75 deletions(-) create mode 100644 MLEM/Formatting/AnimationCode.cs create mode 100644 MLEM/Formatting/FormattingCodeCollection.cs delete mode 100644 MLEM/Formatting/TextAnimation.cs diff --git a/Demos/UiDemo.cs b/Demos/UiDemo.cs index c1fb57a..a713d2e 100644 --- a/Demos/UiDemo.cs +++ b/Demos/UiDemo.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using Coroutine; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -95,18 +96,17 @@ namespace Demos { // adding some custom image formatting codes // note that all added formatting codes need to be lowercase, while their casing doesn't matter when used - TextFormatting.FormattingCodes["grass"] = new FormattingCode(image.Texture); - TextFormatting.FormattingCodes["tree"] = new FormattingCode(tree); + TextFormatting.FormattingCodes[new Regex("grass")] = m => new FormattingCode(image.Texture); + TextFormatting.FormattingCodes[new Regex("tree")] = m => new FormattingCode(tree); // formatting codes can also be sprite animations! var atlas = new UniformTextureAtlas(LoadContent("Textures/Anim"), 4, 4); - TextFormatting.FormattingCodes["walk"] = new FormattingCode(new SpriteAnimation(0.2F, atlas[0, 0], atlas[0, 1], atlas[0, 2], atlas[0, 3])); + TextFormatting.FormattingCodes[new Regex("walk")] = m => new FormattingCode(new SpriteAnimation(0.2F, atlas[0, 0], atlas[0, 1], atlas[0, 2], atlas[0, 3])); root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Additionally, you can create custom formatting codes that contain [Grass] images or [Walk] sprite animations! Note that these images have to be square, or [Tree] bad things happen.")); var animatedPar = 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-esc typing effect by default. Of course, more animations can be added though.")); 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 + OnPressed = e => animatedPar.FormattingCodes.Reset() }); root.AddChild(new VerticalSpace(3)); diff --git a/MLEM.Ui/Elements/Paragraph.cs b/MLEM.Ui/Elements/Paragraph.cs index 47236f0..7852acc 100644 --- a/MLEM.Ui/Elements/Paragraph.cs +++ b/MLEM.Ui/Elements/Paragraph.cs @@ -16,7 +16,7 @@ namespace MLEM.Ui.Elements { private string text; private string splitText; - private Dictionary codeLocations; + public FormattingCodeCollection FormattingCodes { get; private set; } public StyleProp RegularFont; public StyleProp BoldFont; public StyleProp ItalicFont; @@ -36,7 +36,6 @@ namespace MLEM.Ui.Elements { } public bool AutoAdjustWidth; public TextCallback GetTextCallback; - public TimeSpan TimeIntoAnimation; public Paragraph(Anchor anchor, float width, TextCallback textCallback, bool centerText = false) : this(anchor, width, "", centerText) { @@ -60,7 +59,7 @@ namespace MLEM.Ui.Elements { var sc = this.TextScale * this.Scale; this.splitText = this.RegularFont.Value.SplitString(this.text.RemoveFormatting(this.RegularFont.Value), size.X - this.ScaledPadding.Width, sc); - this.codeLocations = this.text.GetFormattingCodes(this.RegularFont.Value); + this.FormattingCodes = this.text.GetFormattingCodes(this.RegularFont.Value); 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); @@ -76,7 +75,8 @@ namespace MLEM.Ui.Elements { base.Update(time); if (this.GetTextCallback != null) this.Text = this.GetTextCallback(this); - this.TimeIntoAnimation += time.ElapsedGameTime; + if (this.FormattingCodes != null) + this.FormattingCodes.Update(time); } public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) { @@ -85,11 +85,11 @@ namespace MLEM.Ui.Elements { 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 - if (this.codeLocations.Count <= 0) { + if (this.FormattingCodes.Count <= 0) { this.RegularFont.Value.DrawString(batch, this.splitText, pos, color, 0, Vector2.Zero, sc, SpriteEffects.None, 0); } else { // if we have formatting codes, we should do it - this.RegularFont.Value.DrawFormattedString(batch, pos, this.splitText, this.codeLocations, color, sc, this.BoldFont.Value, this.ItalicFont.Value, 0, this.TimeIntoAnimation, this.FormatSettings); + this.RegularFont.Value.DrawFormattedString(batch, pos, this.splitText, this.FormattingCodes, color, sc, this.BoldFont.Value, this.ItalicFont.Value, 0, this.FormatSettings); } base.Draw(time, batch, alpha, blendState, samplerState, matrix); } diff --git a/MLEM/Animations/SpriteAnimation.cs b/MLEM/Animations/SpriteAnimation.cs index 615fadc..b53d21b 100644 --- a/MLEM/Animations/SpriteAnimation.cs +++ b/MLEM/Animations/SpriteAnimation.cs @@ -49,13 +49,9 @@ namespace MLEM.Animations { } public void Update(GameTime time) { - this.SetTime(this.TimeIntoAnimation + time.ElapsedGameTime.TotalSeconds * this.SpeedMultiplier); - } - - internal void SetTime(double totalTime) { if (this.IsFinished || this.IsPaused) return; - this.TimeIntoAnimation = totalTime; + this.TimeIntoAnimation += time.ElapsedGameTime.TotalSeconds * this.SpeedMultiplier; if (this.TimeIntoAnimation >= this.TotalTime) { if (!this.IsLooping) { this.IsFinished = true; diff --git a/MLEM/Formatting/AnimationCode.cs b/MLEM/Formatting/AnimationCode.cs new file mode 100644 index 0000000..aaaec6d --- /dev/null +++ b/MLEM/Formatting/AnimationCode.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MLEM.Font; + +namespace MLEM.Formatting { + public class AnimationCode : FormattingCode { + + public static readonly DrawCharacter Default = (code, settings, font, batch, totalText, index, effectStartIndex, charSt, position, color, scale, layerDepth) => { + font.DrawString(batch, charSt, position, color, 0, Vector2.Zero, scale, SpriteEffects.None, layerDepth); + }; + public static readonly DrawCharacter Wobbly = (code, settings, font, batch, totalText, index, effectStartIndex, charSt, position, color, scale, layerDepth) => { + var offset = new Vector2(0, (float) Math.Sin(index + code.Time.TotalSeconds * settings.WobbleModifier) * font.LineHeight * settings.WobbleHeightModifier * scale); + font.DrawString(batch, charSt, position + offset, color, 0, Vector2.Zero, scale, SpriteEffects.None, layerDepth); + }; + public static readonly DrawCharacter Typing = (code, settings, font, batch, totalText, index, effectStartIndex, charSt, position, color, scale, layerDepth) => { + if (code.Time.TotalSeconds * settings.TypingSpeed > index - effectStartIndex + 1) + font.DrawString(batch, charSt, position, color, 0, Vector2.Zero, scale, SpriteEffects.None, layerDepth); + }; + public static readonly AnimationCode DefaultCode = new AnimationCode(Default); + + public readonly DrawCharacter Draw; + public TimeSpan Time; + + public AnimationCode(DrawCharacter draw) : base(Type.Animation) { + this.Draw = draw; + } + + public override void Update(GameTime time) { + this.Time += time.ElapsedGameTime; + } + + public override void Reset() { + this.Time = TimeSpan.Zero; + } + + public delegate void DrawCharacter(AnimationCode code, FormatSettings settings, IGenericFont font, SpriteBatch batch, string totalText, int index, int effectStartIndex, string charSt, Vector2 position, Color color, float scale, float layerDepth); + + } +} \ No newline at end of file diff --git a/MLEM/Formatting/FormattingCode.cs b/MLEM/Formatting/FormattingCode.cs index 46a4d05..832e0d8 100644 --- a/MLEM/Formatting/FormattingCode.cs +++ b/MLEM/Formatting/FormattingCode.cs @@ -10,36 +10,41 @@ namespace MLEM.Formatting { public readonly Color Color; public readonly TextStyle Style; public readonly SpriteAnimation Icon; - public readonly TextAnimation.DrawCharacter Animation; - public FormattingCode(Color color) { - this.Color = color; - this.CodeType = Type.Color; + protected FormattingCode(Type type) { + this.CodeType = type; } - public FormattingCode(TextStyle style) { + public FormattingCode(Color color) : this(Type.Color) { + this.Color = color; + } + + public FormattingCode(TextStyle style) : this(Type.Style) { this.Style = style; - this.CodeType = Type.Style; } public FormattingCode(TextureRegion icon) : this(new SpriteAnimation(0, icon)) { } - public FormattingCode(SpriteAnimation icon) { + public FormattingCode(SpriteAnimation icon) : this(Type.Icon) { this.Icon = icon; - this.CodeType = Type.Icon; - } - - public FormattingCode(TextAnimation.DrawCharacter animation) { - this.Animation = animation; - this.CodeType = Type.Animation; } public virtual string GetReplacementString(IGenericFont font) { return this.CodeType == Type.Icon ? TextFormatting.GetOneEmString(font) : string.Empty; } + public virtual void Update(GameTime time) { + if (this.CodeType == Type.Icon) + this.Icon.Update(time); + } + + public virtual void Reset() { + if (this.CodeType == Type.Icon) + this.Icon.Restart(); + } + public enum Type { Color, diff --git a/MLEM/Formatting/FormattingCodeCollection.cs b/MLEM/Formatting/FormattingCodeCollection.cs new file mode 100644 index 0000000..7b20e61 --- /dev/null +++ b/MLEM/Formatting/FormattingCodeCollection.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; + +namespace MLEM.Formatting { + public class FormattingCodeCollection : Dictionary { + + public void Update(GameTime time) { + foreach (var code in this.Values) + code.Update(time); + } + + public void Reset() { + foreach (var code in this.Values) + code.Reset(); + } + + } +} \ No newline at end of file diff --git a/MLEM/Formatting/TextAnimation.cs b/MLEM/Formatting/TextAnimation.cs deleted file mode 100644 index b1a7622..0000000 --- a/MLEM/Formatting/TextAnimation.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using MLEM.Font; - -namespace MLEM.Formatting { - public static class TextAnimation { - - public static readonly DrawCharacter Default = (settings, font, batch, totalText, index, effectStartIndex, charSt, position, color, scale, layerDepth, timeIntoAnimation) => { - font.DrawString(batch, charSt, position, color, 0, Vector2.Zero, scale, SpriteEffects.None, layerDepth); - }; - - public static readonly DrawCharacter Wobbly = (settings, font, batch, totalText, index, effectStartIndex, charSt, position, color, scale, layerDepth, timeIntoAnimation) => { - var offset = new Vector2(0, (float) Math.Sin(index + timeIntoAnimation.TotalSeconds * settings.WobbleModifier) * font.LineHeight * settings.WobbleHeightModifier * scale); - font.DrawString(batch, charSt, position + offset, color, 0, Vector2.Zero, scale, SpriteEffects.None, layerDepth); - }; - - public static readonly DrawCharacter Typing = (settings, font, batch, totalText, index, effectStartIndex, charSt, position, color, scale, layerDepth, timeIntoAnimation) => { - if (timeIntoAnimation.TotalSeconds * settings.TypingSpeed > index - effectStartIndex + 1) - font.DrawString(batch, charSt, position, color, 0, Vector2.Zero, scale, SpriteEffects.None, layerDepth); - }; - - public delegate void DrawCharacter(FormatSettings settings, IGenericFont font, SpriteBatch batch, string totalText, int index, int effectStartIndex, string charSt, Vector2 position, Color color, float scale, float layerDepth, TimeSpan timeIntoAnimation); - - } -} \ No newline at end of file diff --git a/MLEM/Formatting/TextFormatting.cs b/MLEM/Formatting/TextFormatting.cs index 158ebce..801ccdd 100644 --- a/MLEM/Formatting/TextFormatting.cs +++ b/MLEM/Formatting/TextFormatting.cs @@ -11,7 +11,7 @@ using MLEM.Textures; namespace MLEM.Formatting { public static class TextFormatting { - public static readonly Dictionary FormattingCodes = new Dictionary(); + public static readonly Dictionary> FormattingCodes = new Dictionary>(); private static readonly Dictionary OneEmStrings = new Dictionary(); private static Regex formatRegex; @@ -19,22 +19,22 @@ namespace MLEM.Formatting { SetFormatIndicators('[', ']'); // style codes - FormattingCodes["regular"] = new FormattingCode(TextStyle.Regular); - FormattingCodes["italic"] = new FormattingCode(TextStyle.Italic); - FormattingCodes["bold"] = new FormattingCode(TextStyle.Bold); - FormattingCodes["shadow"] = new FormattingCode(TextStyle.Shadow); + FormattingCodes[new Regex("regular")] = m => new FormattingCode(TextStyle.Regular); + FormattingCodes[new Regex("italic")] = m => new FormattingCode(TextStyle.Italic); + FormattingCodes[new Regex("bold")] = m => new FormattingCode(TextStyle.Bold); + FormattingCodes[new Regex("shadow")] = m => new FormattingCode(TextStyle.Shadow); // color codes var colors = typeof(Color).GetProperties(); foreach (var color in colors) { if (color.GetGetMethod().IsStatic) - FormattingCodes[color.Name.ToLowerInvariant()] = new FormattingCode((Color) color.GetValue(null)); + FormattingCodes[new Regex(color.Name.ToLowerInvariant())] = m => new FormattingCode((Color) color.GetValue(null)); } // animations - FormattingCodes["unanimated"] = new FormattingCode(TextAnimation.Default); - FormattingCodes["wobbly"] = new FormattingCode(TextAnimation.Wobbly); - FormattingCodes["typing"] = new FormattingCode(TextAnimation.Typing); + FormattingCodes[new Regex("unanimated")] = m => new AnimationCode(AnimationCode.Default); + FormattingCodes[new Regex("wobbly")] = m => new AnimationCode(AnimationCode.Wobbly); + FormattingCodes[new Regex("typing")] = m => new AnimationCode(AnimationCode.Typing); } public static void SetFormatIndicators(char opener, char closer) { @@ -62,8 +62,8 @@ namespace MLEM.Formatting { }); } - public static Dictionary GetFormattingCodes(this string s, IGenericFont font) { - var codes = new Dictionary(); + public static FormattingCodeCollection GetFormattingCodes(this string s, IGenericFont font) { + var codes = new FormattingCodeCollection(); var codeLengths = 0; foreach (Match match in formatRegex.Matches(s)) { var code = FromMatch(match); @@ -75,18 +75,18 @@ namespace MLEM.Formatting { return codes; } - public static void DrawFormattedString(this IGenericFont regularFont, SpriteBatch batch, Vector2 pos, string text, Dictionary codeLocations, Color color, float scale, IGenericFont boldFont = null, IGenericFont italicFont = null, float depth = 0, TimeSpan timeIntoAnimation = default, FormatSettings formatSettings = null) { + public static void DrawFormattedString(this IGenericFont regularFont, SpriteBatch batch, Vector2 pos, string text, FormattingCodeCollection codes, Color color, float scale, IGenericFont boldFont = null, IGenericFont italicFont = null, float depth = 0, FormatSettings formatSettings = null) { var settings = formatSettings ?? FormatSettings.Default; var currColor = color; var currFont = regularFont; var currStyle = TextStyle.Regular; - var currAnim = TextAnimation.Default; + var currAnim = AnimationCode.DefaultCode; var animStart = 0; var innerOffset = new Vector2(); for (var i = 0; i < text.Length; i++) { // check if the current character's index has a formatting code - codeLocations.TryGetValue(i, out var code); + codes.TryGetValue(i, out var code); if (code != null) { // if so, apply it switch (code.CodeType) { @@ -108,11 +108,10 @@ namespace MLEM.Formatting { currStyle = code.Style; break; case FormattingCode.Type.Icon: - code.Icon.SetTime(timeIntoAnimation.TotalSeconds * code.Icon.SpeedMultiplier % code.Icon.TotalTime); batch.Draw(code.Icon.CurrentRegion, new RectangleF(pos + innerOffset, new Vector2(regularFont.LineHeight * scale)), color, 0, Vector2.Zero, SpriteEffects.None, depth); break; case FormattingCode.Type.Animation: - currAnim = code.Animation; + currAnim = (AnimationCode) code; animStart = i; break; } @@ -125,8 +124,8 @@ namespace MLEM.Formatting { innerOffset.Y += regularFont.LineHeight * scale; } else { if (currStyle == TextStyle.Shadow) - currAnim(settings, currFont, batch, text, i, animStart, cSt, pos + innerOffset + settings.DropShadowOffset * scale, settings.DropShadowColor, scale, depth, timeIntoAnimation); - currAnim(settings, currFont, batch, text, i, animStart, cSt, pos + innerOffset, currColor, scale, depth, timeIntoAnimation); + currAnim.Draw(currAnim, settings, currFont, batch, text, i, animStart, cSt, pos + innerOffset + settings.DropShadowOffset * scale, settings.DropShadowColor, scale, depth); + currAnim.Draw(currAnim, settings, currFont, batch, text, i, animStart, cSt, pos + innerOffset, currColor, scale, depth); innerOffset.X += regularFont.MeasureString(cSt).X * scale; } } @@ -134,8 +133,12 @@ namespace MLEM.Formatting { private static FormattingCode FromMatch(Capture match) { var rawCode = match.Value.Substring(1, match.Value.Length - 2).ToLowerInvariant(); - FormattingCodes.TryGetValue(rawCode, out var val); - return val; + foreach (var code in FormattingCodes) { + var m = code.Key.Match(rawCode); + if (m.Success) + return code.Value(m); + } + return null; } }