using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Font; using MLEM.Formatting.Codes; using MLEM.Misc; namespace MLEM.Formatting { /// /// A part of a that has a certain list of formatting codes applied. /// public class Token : GenericDataHolder { /// /// The formatting codes that are applied on this token. /// Codes are stored application order, with the first entry in the array being the code that was most recently applied. /// public readonly Code[] AppliedCodes; /// /// The index in the that this token starts at. /// public readonly int Index; /// /// The index in the that this token starts at. /// public readonly int RawIndex; /// /// The substring that this token contains. /// public readonly string Substring; /// /// The string that is displayed by this token. If the tokenized string has been or has been used, this string will contain the newline characters. /// public string DisplayString => this.ModifiedSubstring ?? this.Substring; /// /// The , but split at newline characters /// public string[] SplitDisplayString { get; internal set; } /// /// The substring that this token contains, without the formatting codes removed. /// public readonly string RawSubstring; internal RectangleF[] Area; internal string ModifiedSubstring; internal float[] InnerOffsets; internal Token(Code[] appliedCodes, int index, int rawIndex, string substring, string rawSubstring) { Array.Reverse(appliedCodes); this.AppliedCodes = appliedCodes; this.Index = index; this.RawIndex = rawIndex; this.Substring = substring; this.RawSubstring = rawSubstring; foreach (var code in appliedCodes) code.Tokens.Add(this); } /// /// Get the color that this token will be rendered with /// /// The default color, if none is specified /// The color to render with public Color GetColor(Color defaultPick) { foreach (var code in this.AppliedCodes) { var color = code.GetColor(defaultPick); if (color.HasValue) return color.Value; } return defaultPick; } /// /// Get the font that this token will be rendered with /// /// The default font, if none is specified /// The font to render with public GenericFont GetFont(GenericFont defaultPick) { foreach (var code in this.AppliedCodes) { var font = code.GetFont(defaultPick); if (font != null) return font; } return defaultPick; } /// /// Returns the width of the token itself, including all of the instances that this token contains. /// Note that this method does not return the width of this token's , but only the width that the codes themselves take up. /// /// The font to use for calculating the width. /// The width of this token itself. public float GetSelfWidth(GenericFont font) { var ret = 0F; foreach (var code in this.AppliedCodes) ret += code.GetSelfWidth(font); return ret; } /// /// Draws the token itself, including all of the instances that this token contains. /// Note that, to draw the token's actual string, is used. /// /// The time /// The sprite batch to use /// The position the string is drawn at. /// The offset from the that the current character is drawn at. /// The font to use to draw /// The color to draw with /// The scale to draw with. /// The rotation to draw with. /// The origin to subtract from the position. /// The depth to draw at /// The flipping to draw with. /// The size of the string. public void DrawSelf(GameTime time, SpriteBatch batch, Vector2 stringPos, Vector2 charPosOffset, GenericFont font, Color color, Vector2 scale, float rotation, Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize) { foreach (var code in this.AppliedCodes) code.DrawSelf(time, batch, this, stringPos, charPosOffset, font, color, scale, rotation, origin, depth, effects, stringSize); } /// /// Draws a given code point using this token's formatting options. /// /// The time /// The sprite batch to use /// The code point of the character to draw /// The string representation of the character to draw /// The index within this token that the character is at /// The position the string is drawn at. /// The offset from the that the current character is drawn at. /// The font to use to draw /// The color to draw with /// The scale to draw with. /// The rotation to draw with. /// The origin to subtract from the position. /// The depth to draw at /// The flipping to draw with. /// The size of the string. /// The size of the current character. public void DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, int indexInToken, Vector2 stringPos, Vector2 charPosOffset, GenericFont font, Color color, Vector2 scale, float rotation, Vector2 origin, float depth, SpriteEffects effects, Vector2 stringSize, Vector2 charSize) { foreach (var code in this.AppliedCodes) { if (code.DrawCharacter(time, batch, codePoint, character, this, indexInToken, stringPos, ref charPosOffset, font, ref color, ref scale, ref rotation, ref origin, depth, effects, stringSize, charSize)) return; } // if no code drew, we have to do it ourselves var finalPos = font.TransformSingleCharacter(stringPos, charPosOffset, rotation, origin, scale, effects, stringSize, charSize); font.DrawCharacter(batch, codePoint, character, finalPos, color, rotation, scale, effects, depth); } /// /// Gets a list of rectangles that encompass this token's area. /// This can be used to invoke events when the mouse is hovered over the token, for example. /// /// The position that the string is drawn at /// The scale that the string is drawn at /// A set of rectangles that this token contains public IEnumerable GetArea(Vector2 stringPos, float scale) { return this.Area.Select(a => new RectangleF(stringPos + a.Location * scale, a.Size * scale)); } } }