1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-29 15:58:33 +01:00

document raw content and text formatting

This commit is contained in:
Ellpeck 2020-05-21 12:53:42 +02:00
parent 89646b1c67
commit e9cc9b7d99
26 changed files with 275 additions and 6 deletions

View file

@ -57,6 +57,7 @@ namespace MLEM.Content {
return reader;
}
/// <inheritdoc/>
protected override void ReloadAsset<T>(string originalAssetName, T currentAsset) {
this.Read(originalAssetName, currentAsset);
}
@ -80,6 +81,7 @@ namespace MLEM.Content {
throw new ContentLoadException($"Asset {assetName} not found");
}
/// <inheritdoc/>
public override void Unload() {
foreach (var d in this.disposableAssets)
d.Dispose();
@ -87,6 +89,7 @@ namespace MLEM.Content {
base.Unload();
}
/// <inheritdoc/>
public void Initialize() {
}

View file

@ -1,5 +1,6 @@
using System;
using System.IO;
using Microsoft.Xna.Framework.Content;
namespace MLEM.Content {
/// <summary>
@ -11,14 +12,14 @@ namespace MLEM.Content {
/// Returns if the given type can be loaded by this content reader
/// </summary>
/// <param name="t">The type of asset</param>
/// <returns>If <see cref="t"/> can be loaded by this content reader</returns>
/// <returns>If the type can be loaded by this content reader</returns>
public abstract bool CanRead(Type t);
/// <summary>
/// Reads the content file from disk and returns it.
/// </summary>
/// <param name="manager">The <see cref="RawContentManager"/> that is loading the asset</param>
/// <param name="assetPath">The full path to the asset, starting from the <see cref="RawContentManager.RootDirectory"/></param>
/// <param name="assetPath">The full path to the asset, starting from the <see cref="ContentManager.RootDirectory"/></param>
/// <param name="stream">A stream that leads to this asset</param>
/// <param name="t">The type of asset to load</param>
/// <param name="existing">If this asset is being reloaded, this value contains the previous version of the asset.</param>
@ -50,9 +51,8 @@ namespace MLEM.Content {
/// Reads the content file that is represented by our generic type from disk.
/// </summary>
/// <param name="manager">The <see cref="RawContentManager"/> that is loading the asset</param>
/// <param name="assetPath">The full path to the asset, starting from the <see cref="RawContentManager.RootDirectory"/></param>
/// <param name="assetPath">The full path to the asset, starting from the <see cref="ContentManager.RootDirectory"/></param>
/// <param name="stream">A stream that leads to this asset</param>
/// <param name="t">The type of asset to load</param>
/// <param name="existing">If this asset is being reloaded, this value contains the previous version of the asset.</param>
/// <returns>The loaded asset</returns>
protected abstract T Read(RawContentManager manager, string assetPath, Stream stream, T existing);

View file

@ -3,12 +3,15 @@ using System.IO;
using Microsoft.Xna.Framework.Media;
namespace MLEM.Content {
/// <inheritdoc />
public class SongReader : RawContentReader<Song> {
/// <inheritdoc />
protected override Song Read(RawContentManager manager, string assetPath, Stream stream, Song existing) {
return Song.FromUri(Path.GetFileNameWithoutExtension(assetPath), new Uri(assetPath));
}
/// <inheritdoc />
public override string[] GetFileExtensions() {
return new[] {"ogg", "wav", "mp3"};
}

View file

@ -3,12 +3,15 @@ using System.IO;
using Microsoft.Xna.Framework.Audio;
namespace MLEM.Content {
/// <inheritdoc />
public class SoundEffectReader : RawContentReader<SoundEffect> {
/// <inheritdoc />
protected override SoundEffect Read(RawContentManager manager, string assetPath, Stream stream, SoundEffect existing) {
return SoundEffect.FromStream(stream);
}
/// <inheritdoc />
public override string[] GetFileExtensions() {
return new[] {"ogg", "wav", "mp3"};
}

View file

@ -2,8 +2,10 @@ using System.IO;
using Microsoft.Xna.Framework.Graphics;
namespace MLEM.Content {
/// <inheritdoc />
public class Texture2DReader : RawContentReader<Texture2D> {
/// <inheritdoc />
protected override Texture2D Read(RawContentManager manager, string assetPath, Stream stream, Texture2D existing) {
if (existing != null) {
existing.Reload(stream);
@ -13,6 +15,7 @@ namespace MLEM.Content {
}
}
/// <inheritdoc />
public override string[] GetFileExtensions() {
return new[] {"png", "bmp", "gif", "jpg", "tif", "dds"};
}

View file

@ -3,16 +3,20 @@ using System.IO;
using System.Xml.Serialization;
namespace MLEM.Content {
/// <inheritdoc />
public class XmlReader : RawContentReader {
/// <inheritdoc />
public override bool CanRead(Type t) {
return true;
}
/// <inheritdoc />
public override object Read(RawContentManager manager, string assetPath, Stream stream, Type t, object existing) {
return new XmlSerializer(t).Deserialize(stream);
}
/// <inheritdoc />
public override string[] GetFileExtensions() {
return new[] {"xml"};
}

View file

@ -3,6 +3,9 @@ using System.Globalization;
using Microsoft.Xna.Framework;
namespace MLEM.Extensions {
/// <summary>
/// A set of extensions for dealing with <see cref="Color"/> objects
/// </summary>
public static class ColorExtensions {
/// <summary>

View file

@ -4,6 +4,9 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace MLEM.Extensions {
/// <summary>
/// A set of extensions for dealing with <see cref="GraphicsDevice"/> and <see cref="GraphicsDeviceManager"/>
/// </summary>
public static class GraphicsExtensions {
private static int lastWidth;

View file

@ -3,6 +3,9 @@ using Microsoft.Xna.Framework;
using MLEM.Misc;
namespace MLEM.Extensions {
/// <summary>
/// A set of extensions for dealing with <see cref="float"/>, <see cref="Vector2"/>, <see cref="Vector3"/>, <see cref="Vector4"/>, <see cref="Point"/>, <see cref="Rectangle"/> and <see cref="RectangleF"/>
/// </summary>
public static class NumberExtensions {
/// <inheritdoc cref="Math.Floor(decimal)"/>

View file

@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.Linq;
namespace MLEM.Extensions {
/// <summary>
/// A set of extensions for dealing with <see cref="Random"/>
/// </summary>
public static class RandomExtensions {
/// <summary>

View file

@ -2,8 +2,15 @@ using System;
using Microsoft.Xna.Framework.Audio;
namespace MLEM.Extensions {
/// <summary>
/// A set of extensions for dealing wiht <see cref="SoundEffectInstance"/>
/// </summary>
public static class SoundExtensions {
/// <summary>
/// Stops and plays a sound effect instance in one call
/// </summary>
/// <param name="sound">The sound to stop and play</param>
[Obsolete("When using the .NET Core version of MonoGame, the replay issue has been fixed. Just call Play() instead.")]
public static void Replay(this SoundEffectInstance sound) {
sound.Stop();

View file

@ -4,6 +4,9 @@ using MLEM.Misc;
using MLEM.Textures;
namespace MLEM.Extensions {
/// <summary>
/// A set of extensions for dealing with <see cref="SpriteBatch"/>
/// </summary>
public static class SpriteBatchExtensions {
private static Texture2D blankTexture;

View file

@ -171,11 +171,26 @@ namespace MLEM.Font {
}
/// <summary>
/// An enum that represents the text alignment options for <see cref="GenericFont.DrawString(SpriteBatch,string,Vector2,TextAlign,Color)"/>
/// </summary>
public enum TextAlign {
/// <summary>
/// The text is aligned as normal
/// </summary>
Left,
/// <summary>
/// The position passed represents the center of the resulting string in the x axis
/// </summary>
Center,
/// <summary>
/// The position passed represents the right edge of the resulting string
/// </summary>
Right,
/// <summary>
/// The position passed represents the center of the resulting string, both in the x and y axes
/// </summary>
CenterBothAxes
}

View file

@ -8,6 +8,9 @@ namespace MLEM.Font {
/// <inheritdoc/>
public class GenericSpriteFont : GenericFont {
/// <summary>
/// The <see cref="SpriteFont"/> that is being wrapped by this generic font
/// </summary>
public readonly SpriteFont Font;
/// <inheritdoc/>
public override GenericFont Bold { get; }

View file

@ -1,11 +1,14 @@
using System.Text.RegularExpressions;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class AnimatedCode : Code {
/// <inheritdoc />
public AnimatedCode(Match match, Regex regex) : base(match, regex) {
}
/// <inheritdoc />
public override bool EndsHere(Code other) {
return other is AnimatedCode;
}

View file

@ -5,43 +5,88 @@ using MLEM.Font;
using MLEM.Misc;
namespace MLEM.Formatting.Codes {
/// <summary>
/// An instance of a formatting code that can be used for a <see cref="TextFormatter"/>.
/// To add a new formatting code, see <see cref="TextFormatter.Codes"/>
/// </summary>
public class Code : GenericDataHolder {
/// <summary>
/// The regex that this code was created from
/// </summary>
public readonly Regex Regex;
/// <summary>
/// The match that this code encompasses
/// </summary>
public readonly Match Match;
/// <summary>
/// The token that this formatting code is a part of
/// </summary>
public Token Token { get; internal set; }
/// <summary>
/// Creates a new formatting code based on a formatting code regex and its match.
/// </summary>
/// <param name="match">The match</param>
/// <param name="regex">The regex</param>
protected Code(Match match, Regex regex) {
this.Match = match;
this.Regex = regex;
}
/// <summary>
/// Returns whether this formatting code should end when the passed formatting code starts.
/// If this method returns true, a new <see cref="Token"/> is started at its position.
/// </summary>
/// <param name="other">The code that is started here</param>
/// <returns>If this code should end</returns>
public virtual bool EndsHere(Code other) {
return other.GetType() == this.GetType();
}
/// <inheritdoc cref="Formatting.Token.GetColor"/>
public virtual Color? GetColor(Color defaultPick) {
return null;
}
/// <inheritdoc cref="Formatting.Token.GetFont"/>
public virtual GenericFont GetFont(GenericFont defaultPick) {
return null;
}
/// <summary>
/// Update this formatting code's animations etc.
/// </summary>
/// <param name="time">The game's time</param>
public virtual void Update(GameTime time) {
}
/// <summary>
/// Returns the string that this formatting code should be replaced with.
/// Usually, you'll just want an empty string here, but some formatting codes (like <see cref="ImageCode"/>) require their space to be filled by spaces.
/// </summary>
/// <param name="font">The font that is used</param>
/// <returns>The replacement string for this formatting code</returns>
public virtual string GetReplacementString(GenericFont font) {
return string.Empty;
}
/// <inheritdoc cref="Formatting.Token.DrawCharacter"/>
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;
}
/// <inheritdoc cref="Formatting.Token.DrawSelf"/>
public virtual void DrawSelf(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
}
/// <summary>
/// Creates a new formatting code from the given regex and regex match.
/// <seealso cref="TextFormatter.Codes"/>
/// </summary>
/// <param name="formatter">The text formatter that created this code</param>
/// <param name="match">The match for the code's regex</param>
/// <param name="regex">The regex used to create this code</param>
public delegate Code Constructor(TextFormatter formatter, Match match, Regex regex);
}

View file

@ -2,14 +2,17 @@ using System.Text.RegularExpressions;
using Microsoft.Xna.Framework;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class ColorCode : Code {
private readonly Color? color;
/// <inheritdoc />
public ColorCode(Match match, Regex regex, Color? color) : base(match, regex) {
this.color = color;
}
/// <inheritdoc />
public override Color? GetColor(Color defaultPick) {
return this.color;
}

View file

@ -3,18 +3,22 @@ using System.Text.RegularExpressions;
using MLEM.Font;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class FontCode : Code {
private readonly Func<GenericFont, GenericFont> font;
/// <inheritdoc />
public FontCode(Match match, Regex regex, Func<GenericFont, GenericFont> font) : base(match, regex) {
this.font = font;
}
/// <inheritdoc />
public override GenericFont GetFont(GenericFont defaultPick) {
return this.font?.Invoke(defaultPick);
}
/// <inheritdoc />
public override bool EndsHere(Code other) {
return other is FontCode;
}

View file

@ -9,20 +9,24 @@ using MLEM.Misc;
using MLEM.Textures;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class ImageCode : Code {
private readonly SpriteAnimation image;
private string replacement;
private float gapSize;
/// <inheritdoc />
public ImageCode(Match match, Regex regex, SpriteAnimation image) : base(match, regex) {
this.image = image;
}
/// <inheritdoc />
public override bool EndsHere(Code other) {
return true;
}
/// <inheritdoc />
public override string GetReplacementString(GenericFont font) {
if (this.replacement == null) {
// use non-breaking space so that the image won't be line-splitted
@ -33,10 +37,12 @@ namespace MLEM.Formatting.Codes {
return this.replacement;
}
/// <inheritdoc />
public override void Update(GameTime time) {
this.image.Update(time);
}
/// <inheritdoc />
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));
@ -44,12 +50,22 @@ namespace MLEM.Formatting.Codes {
}
/// <summary>
/// A set of extensions that allow easily adding image formatting codes to a text formatter.
/// </summary>
public static class ImageCodeExtensions {
/// <summary>
/// Adds a new image formatting code to the given text formatter
/// </summary>
/// <param name="formatter">The formatter to add the code to</param>
/// <param name="name">The name of the formatting code. The regex for this code will be between angle brackets.</param>
/// <param name="image">The image to render at the code's position</param>
public static void AddImage(this TextFormatter formatter, string name, TextureRegion image) {
formatter.AddImage(name, new SpriteAnimation(1, image));
}
/// <inheritdoc cref="AddImage(MLEM.Formatting.TextFormatter,string,MLEM.Textures.TextureRegion)"/>
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));
}

View file

@ -5,18 +5,25 @@ using Microsoft.Xna.Framework.Graphics;
using MLEM.Font;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class LinkCode : UnderlineCode {
private readonly Func<Token, bool> isSelected;
/// <inheritdoc />
public LinkCode(Match match, Regex regex, float thickness, float yOffset, Func<Token, bool> isSelected) : base(match, regex, thickness, yOffset) {
this.isSelected = isSelected;
}
/// <summary>
/// Returns true if this link formatting code is currently selected or hovered over, based on the selection function.
/// </summary>
/// <returns>True if this code is currently selected</returns>
public virtual bool IsSelected() {
return this.isSelected(this.Token);
}
/// <inheritdoc />
public override 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) {
// since we inherit from UnderlineCode, we can just call base if selected
return this.IsSelected() && base.DrawCharacter(time, batch, c, cString, indexInToken, ref pos, font, ref color, ref scale, depth);

View file

@ -5,16 +5,19 @@ using MLEM.Extensions;
using MLEM.Font;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class ShadowCode : FontCode {
private readonly Color color;
private readonly Vector2 offset;
/// <inheritdoc />
public ShadowCode(Match match, Regex regex, Color color, Vector2 offset) : base(match, regex, null) {
this.color = color;
this.offset = offset;
}
/// <inheritdoc />
public override 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) {
font.DrawString(batch, cString, pos + this.offset * scale, this.color.CopyAlpha(color), 0, Vector2.Zero, scale, SpriteEffects.None, depth);
// we return false since we still want regular drawing to occur

View file

@ -6,16 +6,19 @@ using MLEM.Font;
using MLEM.Misc;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class UnderlineCode : FontCode {
private readonly float thickness;
private readonly float yOffset;
/// <inheritdoc />
public UnderlineCode(Match match, Regex regex, float thickness, float yOffset) : base(match, regex, null) {
this.thickness = thickness;
this.yOffset = yOffset;
}
/// <inheritdoc />
public override 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) {
// don't underline spaces at the end of lines
if (c == ' ' && this.Token.DisplayString.Length > indexInToken + 1 && this.Token.DisplayString[indexInToken + 1] == '\n')

View file

@ -5,21 +5,29 @@ using Microsoft.Xna.Framework.Graphics;
using MLEM.Font;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class WobblyCode : AnimatedCode {
private readonly float modifier;
private readonly float heightModifier;
/// <summary>
/// The time that this wobbly animation has been running for.
/// To reset its animation progress, reset this value.
/// </summary>
public TimeSpan TimeIntoAnimation;
/// <inheritdoc />
public WobblyCode(Match match, Regex regex, float modifier, float heightModifier) : base(match, regex) {
this.modifier = modifier;
this.heightModifier = heightModifier;
}
/// <inheritdoc />
public override void Update(GameTime time) {
this.TimeIntoAnimation += time.ElapsedGameTime;
}
/// <inheritdoc />
public override 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) {
var offset = new Vector2(0, (float) Math.Sin(this.Token.Index + indexInToken + this.TimeIntoAnimation.TotalSeconds * this.modifier) * font.LineHeight * this.heightModifier * scale);
pos += offset;

View file

@ -10,10 +10,21 @@ using MLEM.Formatting.Codes;
using MLEM.Misc;
namespace MLEM.Formatting {
/// <summary>
/// A text formatter is used for drawing text using <see cref="GenericFont"/> that contains different colors, bold/italic sections and animations.
/// To format a string of text, use the codes as specified in the constructor. To tokenize and render a formatted string, use <see cref="Tokenize"/>.
/// </summary>
public class TextFormatter : GenericDataHolder {
/// <summary>
/// The formatting codes that this text formatter uses.
/// The <see cref="Regex"/> defines how the formatting code should be matched.
/// </summary>
public readonly Dictionary<Regex, Code.Constructor> Codes = new Dictionary<Regex, Code.Constructor>();
/// <summary>
/// Creates a new text formatter with a set of default formatting codes.
/// </summary>
public TextFormatter() {
// font codes
this.Codes.Add(new Regex("<b>"), (f, m, r) => new FontCode(m, r, fnt => fnt.Bold));
@ -37,6 +48,12 @@ namespace MLEM.Formatting {
this.Codes.Add(new Regex("</a>"), (f, m, r) => new AnimatedCode(m, r));
}
/// <summary>
/// Tokenizes a string, returning a tokenized string that is ready for splitting, measuring and drawing.
/// </summary>
/// <param name="font">The font to use for tokenization. Note that this font needs to be the same that will later be used for splitting, measuring and/or drawing.</param>
/// <param name="s">The string to tokenize</param>
/// <returns></returns>
public TokenizedString Tokenize(GenericFont font, string s) {
var tokens = new List<Token>();
var codes = new List<Code>();

View file

@ -8,18 +8,39 @@ using MLEM.Formatting.Codes;
using MLEM.Misc;
namespace MLEM.Formatting {
/// <summary>
/// A part of a <see cref="TokenizedString"/> that has a certain list of formatting codes applied.
/// </summary>
public class Token : GenericDataHolder {
/// <summary>
/// The formatting codes that are applied on this token.
/// </summary>
public readonly Code[] AppliedCodes;
/// <summary>
/// The index in the <see cref="Substring"/> that this token starts at.
/// </summary>
public readonly int Index;
/// <summary>
/// The index in the <see cref="RawSubstring"/> that this token starts at.
/// </summary>
public readonly int RawIndex;
/// <summary>
/// The substring that this token contains.
/// </summary>
public readonly string Substring;
/// <summary>
/// The string that is displayed by this token. If the tokenized string has been split, this string will contain the newline characters.
/// </summary>
public string DisplayString => this.SplitSubstring ?? this.Substring;
/// <summary>
/// The substring that this token contains, without the formatting codes removed.
/// </summary>
public readonly string RawSubstring;
internal RectangleF[] Area;
internal string SplitSubstring;
public Token(Code[] appliedCodes, int index, int rawIndex, string substring, string rawSubstring) {
internal Token(Code[] appliedCodes, int index, int rawIndex, string substring, string rawSubstring) {
this.AppliedCodes = appliedCodes;
this.Index = index;
this.RawIndex = rawIndex;
@ -30,19 +51,53 @@ namespace MLEM.Formatting {
code.Token = this;
}
/// <summary>
/// Get the color that this token will be rendered with
/// </summary>
/// <param name="defaultPick">The default color, if none is specified</param>
/// <returns>The color to render with</returns>
public Color? GetColor(Color defaultPick) {
return this.AppliedCodes.Select(c => c.GetColor(defaultPick)).FirstOrDefault(c => c.HasValue);
}
/// <summary>
/// Get the font that this token will be rendered with
/// </summary>
/// <param name="defaultPick">The default font, if none is specified</param>
/// <returns>The font to render with</returns>
public GenericFont GetFont(GenericFont defaultPick) {
return this.AppliedCodes.Select(c => c.GetFont(defaultPick)).FirstOrDefault(f => f != null);
}
/// <summary>
/// Draws the token itself, including all of the <see cref="Code"/> instances that this token contains.
/// Note that, to draw the token's actual string, <see cref="DrawCharacter"/> is used.
/// </summary>
/// <param name="time">The time</param>
/// <param name="batch">The sprite batch to use</param>
/// <param name="pos">The position to draw the token at</param>
/// <param name="font">The font to use to draw</param>
/// <param name="color">The color to draw with</param>
/// <param name="scale">The scale to draw at</param>
/// <param name="depth">The depth to draw at</param>
public void DrawSelf(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
foreach (var code in this.AppliedCodes)
code.DrawSelf(time, batch, pos, font, color, scale, depth);
}
/// <summary>
/// Draws a given character using this token's formatting options.
/// </summary>
/// <param name="time">The time</param>
/// <param name="batch">The sprite batch to use</param>
/// <param name="c">The character to draw</param>
/// <param name="cString">A single-character string that contains the character to draw</param>
/// <param name="indexInToken">The index within this token that the character is at</param>
/// <param name="pos">The position to draw the token at</param>
/// <param name="font">The font to use to draw</param>
/// <param name="color">The color to draw with</param>
/// <param name="scale">The scale to draw at</param>
/// <param name="depth">The depth to draw at</param>
public void DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, int indexInToken, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
foreach (var code in this.AppliedCodes) {
if (code.DrawCharacter(time, batch, c, cString, indexInToken, ref pos, font, ref color, ref scale, depth))
@ -53,6 +108,14 @@ namespace MLEM.Formatting {
font.DrawString(batch, cString, pos, color, 0, Vector2.Zero, scale, SpriteEffects.None, depth);
}
/// <summary>
/// Gets a list of rectangles that encompass this token's area.
/// Note that more than one rectangle is only returned if the string has been split.
/// This can be used to invoke events when the mouse is hovered over the token, for example.
/// </summary>
/// <param name="stringPos">The position that the string is drawn at</param>
/// <param name="scale">The scale that the string is drawn at</param>
/// <returns>A set of rectangles that this token contains</returns>
public IEnumerable<RectangleF> GetArea(Vector2 stringPos, float scale) {
return this.Area.Select(a => new RectangleF(stringPos + a.Location * scale, a.Size * scale));
}

View file

@ -9,16 +9,36 @@ using MLEM.Formatting.Codes;
using MLEM.Misc;
namespace MLEM.Formatting {
/// <summary>
/// A tokenized string that was created using a <see cref="TextFormatter"/>
/// </summary>
public class TokenizedString : GenericDataHolder {
/// <summary>
/// The raw string that was used to create this tokenized string.
/// </summary>
public readonly string RawString;
/// <summary>
/// The <see cref="RawString"/>, but with formatting codes stripped out.
/// </summary>
public readonly string String;
/// <summary>
/// The string that is actually displayed by this tokenized string.
/// If this string has been <see cref="Split"/>, this string will contain the newline characters.
/// </summary>
public string DisplayString => this.splitString ?? this.String;
/// <summary>
/// The tokens that this tokenized string contains.
/// </summary>
public readonly Token[] Tokens;
/// <summary>
/// All of the formatting codes that are applied over this tokenized string.
/// Note that, to get a formatting code for a certain token, use <see cref="Token.AppliedCodes"/>
/// </summary>
public readonly Code[] AllCodes;
private string splitString;
public TokenizedString(GenericFont font, string rawString, string strg, Token[] tokens) {
internal TokenizedString(GenericFont font, string rawString, string strg, Token[] tokens) {
this.RawString = rawString;
this.String = strg;
this.Tokens = tokens;
@ -27,6 +47,13 @@ namespace MLEM.Formatting {
this.CalculateTokenAreas(font);
}
/// <summary>
/// Splits this tokenized string, inserting newline characters if the width of the string is bigger than the maximum width.
/// <seealso cref="GenericFont.SplitString"/>
/// </summary>
/// <param name="font">The font to use for width calculations</param>
/// <param name="width">The maximum width</param>
/// <param name="scale">The scale to use fr width calculations</param>
public void Split(GenericFont font, float width, float scale) {
// a split string has the same character count as the input string
// but with newline characters added
@ -57,19 +84,33 @@ namespace MLEM.Formatting {
this.CalculateTokenAreas(font);
}
/// <inheritdoc cref="GenericFont.MeasureString(string)"/>
public Vector2 Measure(GenericFont font) {
return font.MeasureString(this.DisplayString);
}
/// <summary>
/// Updates the formatting codes in this formatted string, causing animations to animate etc.
/// </summary>
/// <param name="time">The game's time</param>
public void Update(GameTime time) {
foreach (var code in this.AllCodes)
code.Update(time);
}
/// <summary>
/// Returns the token under the given position.
/// This can be used for hovering effects when the mouse is over a token, etc.
/// </summary>
/// <param name="stringPos">The position that the string is drawn at</param>
/// <param name="target">The position to use for checking the token</param>
/// <param name="scale">The scale that the string is drawn at</param>
/// <returns>The token under the target position</returns>
public Token GetTokenUnderPos(Vector2 stringPos, Vector2 target, float scale) {
return this.Tokens.FirstOrDefault(t => t.GetArea(stringPos, scale).Any(r => r.Contains(target)));
}
/// <inheritdoc cref="GenericFont.DrawString(SpriteBatch,string,Vector2,Color,float,Vector2,float,SpriteEffects,float)"/>
public void Draw(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) {
var innerOffset = new Vector2();
foreach (var token in this.Tokens) {