From 35ee509eac68db6626375475c45668392e7c5834 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Thu, 5 Sep 2019 20:28:05 +0200 Subject: [PATCH] added the ability to display images in paragraphs --- Demos/UiDemo.cs | 12 +++++- MLEM.Ui/Elements/Paragraph.cs | 40 +----------------- MLEM.Ui/Format/FormattingCode.cs | 26 ++++++++++-- MLEM.Ui/Format/TextFormatting.cs | 71 +++++++++++++++++++++++++++----- 4 files changed, 96 insertions(+), 53 deletions(-) diff --git a/Demos/UiDemo.cs b/Demos/UiDemo.cs index bb78dca..5087a46 100644 --- a/Demos/UiDemo.cs +++ b/Demos/UiDemo.cs @@ -11,6 +11,7 @@ using MLEM.Startup; using MLEM.Textures; using MLEM.Ui; using MLEM.Ui.Elements; +using MLEM.Ui.Format; using MLEM.Ui.Style; namespace Demos { @@ -33,6 +34,7 @@ namespace Demos { public override void LoadContent() { this.testTexture = LoadContent("Textures/Test"); this.testPatch = new NinePatch(new TextureRegion(this.testTexture, 0, 8, 24, 24), 8); + var tree = new TextureRegion(LoadContent("Textures/Tree")); base.LoadContent(); // create a new style @@ -90,9 +92,16 @@ 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] and [Bold]Bold[Regular]. \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 + TextFormatting.FormattingCodes["grass"] = new FormattingCode(image.Texture); + TextFormatting.FormattingCodes["tree"] = new FormattingCode(tree); + root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Additionally, you can create custom formatting codes that contain [Grass] images! Note that these images have to be square, or [Tree] bad things happen.")); + root.AddChild(new VerticalSpace(3)); root.AddChild(new Paragraph(Anchor.AutoCenter, 1, "Text input:", true)); root.AddChild(new TextField(Anchor.AutoLeft, new Vector2(1, 10)) { @@ -170,8 +179,7 @@ namespace Demos { PositionOffset = new Vector2(0, 1) }); - var region = new TextureRegion(LoadContent("Textures/Tree")); - root.AddChild(ElementHelper.ImageButton(Anchor.AutoLeft, new Vector2(1, 10), region, "Button with image")).PositionOffset = new Vector2(0, 1); + root.AddChild(ElementHelper.ImageButton(Anchor.AutoLeft, new Vector2(1, 10), tree, "Button with image")).PositionOffset = new Vector2(0, 1); // Below are some querying examples that help you find certain elements easily diff --git a/MLEM.Ui/Elements/Paragraph.cs b/MLEM.Ui/Elements/Paragraph.cs index e8c931c..7dd650a 100644 --- a/MLEM.Ui/Elements/Paragraph.cs +++ b/MLEM.Ui/Elements/Paragraph.cs @@ -77,44 +77,8 @@ namespace MLEM.Ui.Elements { if (this.codeLocations.Count <= 0) { this.regularFont.DrawString(batch, this.splitText, pos, this.TextColor * alpha, 0, Vector2.Zero, sc, SpriteEffects.None, 0); } else { - // if we have formatting codes, we need to go through each index and see how it should be drawn - var characterCounter = 0; - var currColor = this.TextColor; - var currFont = this.regularFont; - - var innerOffset = new Vector2(); - foreach (var c in this.splitText) { - // check if the current character's index has a formatting code - this.codeLocations.TryGetValue(characterCounter, out var code); - if (code != null) { - // if so, apply it - if (code.IsColorCode) { - currColor = code.Color; - } else { - switch (code.Style) { - case TextStyle.Regular: - currFont = this.regularFont; - break; - case TextStyle.Bold: - currFont = this.boldFont; - break; - case TextStyle.Italic: - currFont = this.italicFont; - break; - } - } - } - characterCounter++; - - var cSt = c.ToString(); - if (c == '\n') { - innerOffset.X = 0; - innerOffset.Y += this.regularFont.LineHeight * sc; - } else { - currFont.DrawString(batch, cSt, pos + innerOffset, currColor * alpha, 0, Vector2.Zero, sc, SpriteEffects.None, 0); - innerOffset.X += this.regularFont.MeasureString(cSt).X * sc; - } - } + // if we have formatting codes, we should do it + this.regularFont.DrawFormattedString(batch, pos, this.splitText, this.codeLocations, this.TextColor * alpha, sc, this.boldFont, this.italicFont); } base.Draw(time, batch, alpha); } diff --git a/MLEM.Ui/Format/FormattingCode.cs b/MLEM.Ui/Format/FormattingCode.cs index 840b957..9c81066 100644 --- a/MLEM.Ui/Format/FormattingCode.cs +++ b/MLEM.Ui/Format/FormattingCode.cs @@ -1,20 +1,40 @@ +using System; using Microsoft.Xna.Framework; +using MLEM.Textures; namespace MLEM.Ui.Format { public class FormattingCode { + public readonly Type CodeType; public readonly Color Color; public readonly TextStyle Style; - public readonly bool IsColorCode; + public readonly TextureRegion Icon; public FormattingCode(Color color) { this.Color = color; - this.IsColorCode = true; + this.CodeType = Type.Color; } public FormattingCode(TextStyle style) { this.Style = style; - this.IsColorCode = false; + this.CodeType = Type.Style; + } + + public FormattingCode(TextureRegion icon) { + this.Icon = icon; + this.CodeType = Type.Icon; + } + + public string GetReplacementString() { + return this.CodeType == Type.Icon ? TextFormatting.OneEmString : string.Empty; + } + + public enum Type { + + Color, + Style, + Icon + } } diff --git a/MLEM.Ui/Format/TextFormatting.cs b/MLEM.Ui/Format/TextFormatting.cs index 57ad55c..5ecd40d 100644 --- a/MLEM.Ui/Format/TextFormatting.cs +++ b/MLEM.Ui/Format/TextFormatting.cs @@ -2,13 +2,17 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MLEM.Extensions; +using MLEM.Font; +using MLEM.Textures; namespace MLEM.Ui.Format { public static class TextFormatting { - private static Regex formatRegex; - public static readonly Dictionary FormattingCodes = new Dictionary(); + public static string OneEmString = " "; + private static Regex formatRegex; static TextFormatting() { SetFormatIndicators('[', ']'); @@ -33,22 +37,69 @@ namespace MLEM.Ui.Format { } public static string RemoveFormatting(this string s) { - return formatRegex.Replace(s, string.Empty); + return formatRegex.Replace(s, match => FromMatch(match).GetReplacementString()); } - public static Dictionary GetFormattingCodes(this string s, bool indicesIgnoreCode = true) { + public static Dictionary GetFormattingCodes(this string s) { var codes = new Dictionary(); var codeLengths = 0; foreach (Match match in formatRegex.Matches(s)) { - var rawCode = match.Value.Substring(1, match.Value.Length - 2).ToLowerInvariant(); - codes[match.Index - codeLengths] = FormattingCodes[rawCode]; - // if indices of formatting codes should ignore the codes themselves, then the lengths of all - // of the codes we have sound so far needs to be subtracted from the found code's index - if (indicesIgnoreCode) - codeLengths += match.Length; + var code = FromMatch(match); + codes[match.Index - codeLengths] = code; + codeLengths += match.Length - code.GetReplacementString().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) { + var characterCounter = 0; + var currColor = color; + var currFont = regularFont; + + var innerOffset = new Vector2(); + foreach (var c in text) { + // check if the current character's index has a formatting code + codeLocations.TryGetValue(characterCounter, out var code); + if (code != null) { + // if so, apply it + if (code.CodeType == FormattingCode.Type.Color) { + currColor = code.Color.CopyAlpha(color); + } else if (code.CodeType == 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; + } + } else if (code.CodeType == FormattingCode.Type.Icon) { + var iconSc = new Vector2(1F / code.Icon.Width, 1F / code.Icon.Height) * regularFont.LineHeight * scale; + batch.Draw(code.Icon, pos + innerOffset, color, 0, Vector2.Zero, iconSc, SpriteEffects.None, depth); + } + } + characterCounter++; + + var cSt = c.ToString(); + if (c == '\n') { + innerOffset.X = 0; + innerOffset.Y += regularFont.LineHeight * scale; + } else { + currFont.DrawString(batch, cSt, pos + innerOffset, currColor, 0, Vector2.Zero, scale, SpriteEffects.None, depth); + // we measure the string with the regular font here so that previously split + // strings don't get too long with a bolder font. This shouldn't effect visuals too much + innerOffset.X += regularFont.MeasureString(cSt).X * scale; + } + } + } + + private static FormattingCode FromMatch(Capture match) { + var rawCode = match.Value.Substring(1, match.Value.Length - 2).ToLowerInvariant(); + return FormattingCodes[rawCode]; + } + } } \ No newline at end of file