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

Compare commits

...

2 commits

Author SHA1 Message Date
Ell
3968f7dfae Added a simple outline formatting code 2023-02-23 15:18:07 +01:00
Ell
d8314877c8 improved text formatting demo 2023-02-23 14:33:03 +01:00
11 changed files with 279 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

@ -41,6 +41,27 @@
/processorParam:TextureFormat=Compressed /processorParam:TextureFormat=Compressed
/build:Fonts/TestFontItalic.spritefont /build:Fonts/TestFontItalic.spritefont
#begin Fonts/Roboto.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
/processorParam:PremultiplyAlpha=True
/processorParam:TextureFormat=Compressed
/build:Fonts/Roboto.spritefont
#begin Fonts/RobotoBold.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
/processorParam:PremultiplyAlpha=True
/processorParam:TextureFormat=Compressed
/build:Fonts/RobotoBold.spritefont
#begin Fonts/RobotoItalic.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
/processorParam:PremultiplyAlpha=True
/processorParam:TextureFormat=Compressed
/build:Fonts/RobotoItalic.spritefont
#begin Markdown.md #begin Markdown.md
/copy:Markdown.md /copy:Markdown.md

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>RobotoRegular.ttf</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>RobotoBold.ttf</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

Binary file not shown.

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>RobotoItalic.ttf</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<DefaultCharacter>*</DefaultCharacter>
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#591;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

Binary file not shown.

Binary file not shown.

View file

@ -15,18 +15,25 @@ 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" +
"Additionally, the text formatter has various methods for interacting with the text, like custom behaviors when hovering over certain parts, and more."; "Additionally, the text formatter has various methods for interacting with the text, like custom behaviors when hovering over certain parts, and more.";
private const float Scale = 0.5F; private const float DefaultScale = 0.5F;
private const float Width = 0.9F; private const float WidthMultiplier = 0.9F;
private TextFormatter formatter; private TextFormatter formatter;
private TokenizedString tokenizedText; private TokenizedString tokenizedText;
private GenericFont font; private GenericFont font;
private bool drawBounds; private bool drawBounds;
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) {}
@ -34,13 +41,16 @@ 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(
Demo.LoadContent<SpriteFont>("Fonts/TestFont"), Demo.LoadContent<SpriteFont>("Fonts/Roboto"),
Demo.LoadContent<SpriteFont>("Fonts/TestFontBold"), Demo.LoadContent<SpriteFont>("Fonts/RobotoBold"),
Demo.LoadContent<SpriteFont>("Fonts/TestFontItalic")); Demo.LoadContent<SpriteFont>("Fonts/RobotoItalic"));
// adding the image code used in the example to it // adding the image code used in the example to it
var testTexture = Demo.LoadContent<Texture2D>("Textures/Test"); var testTexture = Demo.LoadContent<Texture2D>("Textures/Test");
@ -49,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.Width, TextFormattingDemo.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) {
@ -58,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, TextFormattingDemo.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
@ -66,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, TextFormattingDemo.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, TextFormattingDemo.Scale, 0); this.tokenizedText.Draw(time, this.SpriteBatch, pos, this.font, Color.White, this.Scale, 0);
this.SpriteBatch.End(); this.SpriteBatch.End();
} }
@ -92,7 +102,7 @@ namespace Demos {
private void OnResize(object sender, EventArgs e) { private void OnResize(object sender, EventArgs e) {
// 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.Width, TextFormattingDemo.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