diff --git a/Demos/UiDemo.cs b/Demos/UiDemo.cs index c1fb57a..9e1b7fc 100644 --- a/Demos/UiDemo.cs +++ b/Demos/UiDemo.cs @@ -91,7 +91,7 @@ namespace Demos { root.AddChild(new VerticalSpace(3)); // a paragraph with formatting codes. To see them all or to add more, check the TextFormatting class - 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] and [Shadow]Drop Shadow'd[Regular]. \n[Italic]Even [CornflowerBlue]Cornflower Blue[White] works!")); + 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!")); // adding some custom image formatting codes // note that all added formatting codes need to be lowercase, while their casing doesn't matter when used diff --git a/MLEM.Ui/Elements/Paragraph.cs b/MLEM.Ui/Elements/Paragraph.cs index 47236f0..42e7c53 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 Formatting; public StyleProp RegularFont; public StyleProp BoldFont; public StyleProp ItalicFont; @@ -60,7 +60,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.Formatting = 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); @@ -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.Formatting.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.Formatting, color, sc, this.BoldFont.Value, this.ItalicFont.Value, 0, this.TimeIntoAnimation, this.FormatSettings); } base.Draw(time, batch, alpha, blendState, samplerState, matrix); } diff --git a/MLEM/Formatting/FormattingCodeCollection.cs b/MLEM/Formatting/FormattingCodeCollection.cs new file mode 100644 index 0000000..f3ee385 --- /dev/null +++ b/MLEM/Formatting/FormattingCodeCollection.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +namespace MLEM.Formatting { + public class FormattingCodeCollection : Dictionary> { + + } +} \ No newline at end of file diff --git a/MLEM/Formatting/TextFormatting.cs b/MLEM/Formatting/TextFormatting.cs index 158ebce..31a2165 100644 --- a/MLEM/Formatting/TextFormatting.cs +++ b/MLEM/Formatting/TextFormatting.cs @@ -58,24 +58,29 @@ namespace MLEM.Formatting { public static string RemoveFormatting(this string s, IGenericFont font) { return formatRegex.Replace(s, match => { var code = FromMatch(match); - return code != null ? code.GetReplacementString(font) : string.Empty; + return code != null ? code.GetReplacementString(font) : match.Value; }); } - 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); if (code == null) continue; - codes[match.Index - codeLengths] = code; + var index = match.Index - codeLengths; + if (codes.TryGetValue(index, out var curr)) { + curr.Add(code); + } else { + codes.Add(index, new List {code}); + } codeLengths += match.Length - code.GetReplacementString(font).Length; } 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 unformattedText, FormattingCodeCollection formatting, Color color, float scale, IGenericFont boldFont = null, IGenericFont italicFont = null, float depth = 0, TimeSpan timeIntoAnimation = default, FormatSettings formatSettings = null) { var settings = formatSettings ?? FormatSettings.Default; var currColor = color; var currFont = regularFont; @@ -84,49 +89,50 @@ namespace MLEM.Formatting { var animStart = 0; var innerOffset = new Vector2(); - for (var i = 0; i < text.Length; i++) { + for (var i = 0; i < unformattedText.Length; i++) { // check if the current character's index has a formatting code - codeLocations.TryGetValue(i, out var code); - if (code != null) { - // if so, apply it - switch (code.CodeType) { - case FormattingCode.Type.Color: - currColor = code.Color.CopyAlpha(color); - break; - case FormattingCode.Type.Style: - switch (code.Style) { - case TextStyle.Regular: - currFont = regularFont; - break; - case TextStyle.Bold: - currFont = boldFont ?? regularFont; - break; - case TextStyle.Italic: - currFont = italicFont ?? regularFont; - break; - } - 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; - animStart = i; - break; + if (formatting.TryGetValue(i, out var codes)) { + foreach (var code in codes) { + // if so, apply it + switch (code.CodeType) { + case FormattingCode.Type.Color: + currColor = code.Color.CopyAlpha(color); + break; + case FormattingCode.Type.Style: + switch (code.Style) { + case TextStyle.Regular: + currFont = regularFont; + break; + case TextStyle.Bold: + currFont = boldFont ?? regularFont; + break; + case TextStyle.Italic: + currFont = italicFont ?? regularFont; + break; + } + 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; + animStart = i; + break; + } } } - var c = text[i]; + var c = unformattedText[i]; var cSt = c.ToString(); if (c == '\n') { innerOffset.X = 0; 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(settings, currFont, batch, unformattedText, i, animStart, cSt, pos + innerOffset + settings.DropShadowOffset * scale, settings.DropShadowColor, scale, depth, timeIntoAnimation); + currAnim(settings, currFont, batch, unformattedText, i, animStart, cSt, pos + innerOffset, currColor, scale, depth, timeIntoAnimation); innerOffset.X += regularFont.MeasureString(cSt).X * scale; } }