1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-22 04:53:29 +01:00

Added a simple outline formatting code

This commit is contained in:
Ell 2023-02-23 15:18:07 +01:00
parent d8314877c8
commit 3968f7dfae
4 changed files with 73 additions and 12 deletions

View file

@ -13,6 +13,9 @@ Jump to version:
## 6.2.0 (In Development) ## 6.2.0 (In Development)
### MLEM ### MLEM
Additions
- Added a simple outline formatting code
Fixes Fixes
- Fixed control characters being included in TextInput - Fixed control characters being included in TextInput
- Fixed TextInputs behaving incorrectly when switching between multiline and single-line modes - Fixed TextInputs behaving incorrectly when switching between multiline and single-line modes

View file

@ -15,7 +15,7 @@ namespace Demos {
private const string Text = private const string Text =
"MLEM's text formatting system allows for various <b>formatting codes</b> to be applied in the middle of a string. Here's a demonstration of some of them.\n\n" + "MLEM's text formatting system allows for various <b>formatting codes</b> to be applied in the middle of a string. Here's a demonstration of some of them.\n\n" +
"You can write in <b>bold</i>, <i>italics</i>, <u>with an underline</u>, <st>strikethrough</st>, with a <s #000000 4>drop shadow</s> whose <s #ff0000 4>color</s> and <s #000000 10>offset</s> you can modify in each application of the code, or with various types of <b>combined <c Pink>formatting</c> codes</b>.\n\n" + "You can write in <b>bold</i>, <i>italics</i>, <u>with an underline</u>, <st>strikethrough</st>, with a <s>drop shadow</s> whose <s #ff0000 4>color</s> and <s #000000 10>offset</s> you can modify in each application of the code, with an <o>outline</o> that you can also <o #ff0000 4>modify</o> <o #ff00ff 2>dynamically</o>, or with various types of <b>combined <c Pink>formatting</c> codes</b>.\n\n" +
"You can apply <c CornflowerBlue>custom</c> <c Yellow>colors</c> to text, including all default <c Orange>MonoGame colors</c> and <c #aabb00>inline custom colors</c>.\n\n" + "You can apply <c CornflowerBlue>custom</c> <c Yellow>colors</c> to text, including all default <c Orange>MonoGame colors</c> and <c #aabb00>inline custom colors</c>.\n\n" +
"You can also use animations like <a wobbly>a wobbly one</a>, as well as create custom ones using the <a wobbly>Code class</a>.\n\n" + "You can also use animations like <a wobbly>a wobbly one</a>, as well as create custom ones using the <a wobbly>Code class</a>.\n\n" +
"You can also display <i grass> icons in your text, and use super<sup>script</sup> or sub<sub>script</sub> formatting!\n\n" + "You can also display <i grass> icons in your text, and use super<sup>script</sup> or sub<sub>script</sub> formatting!\n\n" +
@ -27,7 +27,13 @@ namespace Demos {
private TokenizedString tokenizedText; private TokenizedString tokenizedText;
private GenericFont font; private GenericFont font;
private bool drawBounds; private bool drawBounds;
private float scale = TextFormattingDemo.DefaultScale; private float Scale {
get {
// calculate our scale based on how much larger the window is, so that the text scales with the window
var viewport = new Rectangle(0, 0, this.Game.Window.ClientBounds.Width, this.Game.Window.ClientBounds.Height);
return TextFormattingDemo.DefaultScale * Math.Min(viewport.Width / 1280F, viewport.Height / 720F);
}
}
public TextFormattingDemo(MlemGame game) : base(game) {} public TextFormattingDemo(MlemGame game) : base(game) {}
@ -35,7 +41,10 @@ namespace Demos {
this.Game.Window.ClientSizeChanged += this.OnResize; this.Game.Window.ClientSizeChanged += this.OnResize;
// creating a new text formatter as well as a generic font to draw with // creating a new text formatter as well as a generic font to draw with
this.formatter = new TextFormatter(); this.formatter = new TextFormatter {
DefaultShadowOffset = new Vector2(4),
DefaultOutlineThickness = 4
};
// GenericFont and its subtypes are wrappers around various font classes, including SpriteFont, MonoGame.Extended's BitmapFont and FontStashSharp // GenericFont and its subtypes are wrappers around various font classes, including SpriteFont, MonoGame.Extended's BitmapFont and FontStashSharp
// supplying a bold and italic version of the font here allows for the bold and italic formatting codes to be used // supplying a bold and italic version of the font here allows for the bold and italic formatting codes to be used
this.font = new GenericSpriteFont( this.font = new GenericSpriteFont(
@ -50,7 +59,7 @@ namespace Demos {
// tokenizing our text and splitting it to fit the screen // tokenizing our text and splitting it to fit the screen
// we specify our text alignment here too, so that all data is cached correctly for display // we specify our text alignment here too, so that all data is cached correctly for display
this.tokenizedText = this.formatter.Tokenize(this.font, TextFormattingDemo.Text, TextAlignment.Center); this.tokenizedText = this.formatter.Tokenize(this.font, TextFormattingDemo.Text, TextAlignment.Center);
this.tokenizedText.Split(this.font, this.GraphicsDevice.Viewport.Width * TextFormattingDemo.WidthMultiplier, this.scale, TextAlignment.Center); this.tokenizedText.Split(this.font, this.GraphicsDevice.Viewport.Width * TextFormattingDemo.WidthMultiplier, this.Scale, TextAlignment.Center);
} }
public override void DoDraw(GameTime time) { public override void DoDraw(GameTime time) {
@ -59,7 +68,7 @@ namespace Demos {
// we draw the tokenized text in the center of the screen // we draw the tokenized text in the center of the screen
// since the text is already center-aligned, we only need to align it on the y axis here // since the text is already center-aligned, we only need to align it on the y axis here
var size = this.tokenizedText.GetArea(Vector2.Zero, this.scale).Size; var size = this.tokenizedText.GetArea(Vector2.Zero, this.Scale).Size;
var pos = new Vector2(this.GraphicsDevice.Viewport.Width / 2, (this.GraphicsDevice.Viewport.Height - size.Y) / 2); var pos = new Vector2(this.GraphicsDevice.Viewport.Width / 2, (this.GraphicsDevice.Viewport.Height - size.Y) / 2);
// draw bounds, which can be toggled with B in this demo // draw bounds, which can be toggled with B in this demo
@ -67,13 +76,13 @@ namespace Demos {
var blank = this.SpriteBatch.GetBlankTexture(); var blank = this.SpriteBatch.GetBlankTexture();
this.SpriteBatch.Draw(blank, new RectangleF(pos - new Vector2(size.X / 2, 0), size), Color.Red * 0.25F); this.SpriteBatch.Draw(blank, new RectangleF(pos - new Vector2(size.X / 2, 0), size), Color.Red * 0.25F);
foreach (var token in this.tokenizedText.Tokens) { foreach (var token in this.tokenizedText.Tokens) {
foreach (var area in token.GetArea(pos, this.scale)) foreach (var area in token.GetArea(pos, this.Scale))
this.SpriteBatch.Draw(blank, area, Color.Black * 0.25F); this.SpriteBatch.Draw(blank, area, Color.Black * 0.25F);
} }
} }
// draw the text itself // draw the text itself
this.tokenizedText.Draw(time, this.SpriteBatch, pos, this.font, Color.White, this.scale, 0); this.tokenizedText.Draw(time, this.SpriteBatch, pos, this.font, Color.White, this.Scale, 0);
this.SpriteBatch.End(); this.SpriteBatch.End();
} }
@ -91,13 +100,9 @@ namespace Demos {
} }
private void OnResize(object sender, EventArgs e) { private void OnResize(object sender, EventArgs e) {
// scale our text based on window size
var viewport = new Rectangle(0, 0, this.Game.Window.ClientBounds.Width, this.Game.Window.ClientBounds.Height);
this.scale = TextFormattingDemo.DefaultScale * Math.Min(viewport.Width / 1280F, viewport.Height / 720F);
// re-split our text if the window resizes, since it depends on the window size // re-split our text if the window resizes, since it depends on the window size
// this doesn't require re-tokenization of the text, since TokenizedString also stores the un-split version // this doesn't require re-tokenization of the text, since TokenizedString also stores the un-split version
this.tokenizedText.Split(this.font, this.GraphicsDevice.Viewport.Width * TextFormattingDemo.WidthMultiplier, this.scale, TextAlignment.Center); this.tokenizedText.Split(this.font, this.GraphicsDevice.Viewport.Width * TextFormattingDemo.WidthMultiplier, this.Scale, TextAlignment.Center);
} }
} }

View file

@ -0,0 +1,34 @@
using System;
using System.Text.RegularExpressions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
using MLEM.Font;
using MLEM.Misc;
namespace MLEM.Formatting.Codes {
/// <inheritdoc />
public class OutlineCode : Code {
private readonly Color color;
private readonly float thickness;
private readonly bool diagonals;
/// <inheritdoc />
public OutlineCode(Match match, Regex regex, Color color, float thickness, bool diagonals) : base(match, regex) {
this.color = color;
this.thickness = thickness;
this.diagonals = diagonals;
}
/// <inheritdoc />
public override bool DrawCharacter(GameTime time, SpriteBatch batch, int codePoint, string character, Token token, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) {
foreach (var dir in this.diagonals ? Direction2Helper.AllExceptNone : Direction2Helper.Adjacent) {
var offset = Vector2.Normalize(dir.Offset().ToVector2()) * (this.thickness * scale);
font.DrawString(batch, character, pos + offset, this.color.CopyAlpha(color), 0, Vector2.Zero, scale, SpriteEffects.None, depth);
}
return false;
}
}
}

View file

@ -73,6 +73,21 @@ namespace MLEM.Formatting {
/// Note that this value only has an effect on the default formatting codes created through the <see cref="TextFormatter(bool, bool, bool, bool)"/> constructor. /// Note that this value only has an effect on the default formatting codes created through the <see cref="TextFormatter(bool, bool, bool, bool)"/> constructor.
/// </summary> /// </summary>
public float DefaultWobblyHeight = 1 / 8F; public float DefaultWobblyHeight = 1 / 8F;
/// <summary>
/// The default outline thickness used by this text formatter, which determines how the default <see cref="OutlineCode"/> is drawn if no custom value is used.
/// Note that this value only has an effect on the default formatting codes created through the <see cref="TextFormatter(bool, bool, bool, bool)"/> constructor.
/// </summary>
public float DefaultOutlineThickness = 2;
/// <summary>
/// The default outline color used by this text formatter, which determines how the default <see cref="OutlineCode"/> is drawn if no custom value is used.
/// Note that this value only has an effect on the default formatting codes created through the <see cref="TextFormatter(bool, bool, bool, bool)"/> constructor.
/// </summary>
public Color DefaultOutlineColor = Color.Black;
/// <summary>
/// Whether the default outline used by this text formatter should also draw outlines diagonally, which determines how the default <see cref="OutlineCode"/> is drawn if no custom value is used. Non-diagonally drawn outlines might generally look better when using a pixelart font.
/// Note that this value only has an effect on the default formatting codes created through the <see cref="TextFormatter(bool, bool, bool, bool)"/> constructor.
/// </summary>
public bool OutlineDiagonals = true;
/// <summary> /// <summary>
/// Creates a new text formatter with an optional set of default formatting codes. /// Creates a new text formatter with an optional set of default formatting codes.
@ -95,6 +110,10 @@ namespace MLEM.Formatting {
float.TryParse(m.Groups[1].Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var off) ? off : this.DefaultSubOffset)); float.TryParse(m.Groups[1].Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var off) ? off : this.DefaultSubOffset));
this.Codes.Add(new Regex(@"<sup(?: ([+-.0-9]+))?>"), (f, m, r) => new SubSupCode(m, r, this.Codes.Add(new Regex(@"<sup(?: ([+-.0-9]+))?>"), (f, m, r) => new SubSupCode(m, r,
float.TryParse(m.Groups[1].Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var off) ? -off : this.DefaultSupOffset)); float.TryParse(m.Groups[1].Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var off) ? -off : this.DefaultSupOffset));
this.Codes.Add(new Regex(@"<o(?: #([0-9\w]{6,8}) (([+-.0-9]*)))?>"), (f, m, r) => new OutlineCode(m, r,
m.Groups[1].Success ? ColorHelper.FromHexString(m.Groups[1].Value) : this.DefaultOutlineColor,
float.TryParse(m.Groups[2].Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var thickness) ? thickness : this.DefaultOutlineThickness,
this.OutlineDiagonals));
} }
// color codes // color codes