mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-22 20:58:34 +01:00
added text alignment options to tokenized strings and paragraphs
This commit is contained in:
parent
d1ce9412a2
commit
14940d39c5
7 changed files with 188 additions and 72 deletions
|
@ -6,6 +6,7 @@ using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using MLEM.Extensions;
|
using MLEM.Extensions;
|
||||||
using MLEM.Font;
|
using MLEM.Font;
|
||||||
|
using MLEM.Formatting;
|
||||||
using MLEM.Formatting.Codes;
|
using MLEM.Formatting.Codes;
|
||||||
using MLEM.Misc;
|
using MLEM.Misc;
|
||||||
using MLEM.Startup;
|
using MLEM.Startup;
|
||||||
|
@ -191,6 +192,14 @@ namespace Demos {
|
||||||
this.root.AddChild(new VerticalSpace(3));
|
this.root.AddChild(new VerticalSpace(3));
|
||||||
this.root.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 10), "Disabled button", "This button can't be clicked or moved to using automatic navigation") {IsDisabled = true}).PositionOffset = new Vector2(0, 1);
|
this.root.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 10), "Disabled button", "This button can't be clicked or moved to using automatic navigation") {IsDisabled = true}).PositionOffset = new Vector2(0, 1);
|
||||||
|
|
||||||
|
const string alignText = "Paragraphs can have <c CornflowerBlue><l Left>left</l></c> aligned text, <c CornflowerBlue><l Right>right</l></c> aligned text and <c CornflowerBlue><l Center>center</l></c> aligned text.";
|
||||||
|
this.root.AddChild(new VerticalSpace(3));
|
||||||
|
var alignPar = this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, alignText));
|
||||||
|
alignPar.LinkAction = (l, c) => {
|
||||||
|
if (Enum.TryParse<TextAlignment>(c.Match.Groups[1].Value, out var alignment))
|
||||||
|
alignPar.Alignment = alignment;
|
||||||
|
};
|
||||||
|
|
||||||
this.root.AddChild(new VerticalSpace(3));
|
this.root.AddChild(new VerticalSpace(3));
|
||||||
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "The code for this demo contains some examples for how to query element data. This is the output of that:"));
|
this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "The code for this demo contains some examples for how to query element data. This is the output of that:"));
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ namespace MLEM.Ui.Elements {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Paragraph : Element {
|
public class Paragraph : Element {
|
||||||
|
|
||||||
private string text;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The font that this paragraph draws text with.
|
/// The font that this paragraph draws text with.
|
||||||
/// To set its bold and italic font, use <see cref="GenericFont.Bold"/> and <see cref="GenericFont.Italic"/>.
|
/// To set its bold and italic font, use <see cref="GenericFont.Bold"/> and <see cref="GenericFont.Italic"/>.
|
||||||
|
@ -84,6 +83,20 @@ namespace MLEM.Ui.Elements {
|
||||||
/// By default, <see cref="MlemPlatform.OpenLinkOrFile"/> is executed.
|
/// By default, <see cref="MlemPlatform.OpenLinkOrFile"/> is executed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<Link, LinkCode> LinkAction;
|
public Action<Link, LinkCode> LinkAction;
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="TextAlignment"/> that this paragraph's text should be rendered with
|
||||||
|
/// </summary>
|
||||||
|
public TextAlignment Alignment {
|
||||||
|
get => this.alignment;
|
||||||
|
set {
|
||||||
|
this.alignment = value;
|
||||||
|
this.SetAreaDirty();
|
||||||
|
this.TokenizedText = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string text;
|
||||||
|
private TextAlignment alignment;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new paragraph with the given settings.
|
/// Creates a new paragraph with the given settings.
|
||||||
|
@ -128,10 +141,10 @@ namespace MLEM.Ui.Elements {
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) {
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) {
|
||||||
var pos = this.DisplayArea.Location;
|
var pos = this.DisplayArea.Location + new Vector2(GetAlignmentOffset(), 0);
|
||||||
var sc = this.TextScale * this.TextScaleMultiplier * this.Scale;
|
var sc = this.TextScale * this.TextScaleMultiplier * this.Scale;
|
||||||
var color = this.TextColor.OrDefault(Color.White) * alpha;
|
var color = this.TextColor.OrDefault(Color.White) * alpha;
|
||||||
this.TokenizedText.Draw(time, batch, pos, this.RegularFont, color, sc, 0);
|
this.TokenizedText.Draw(time, batch, pos, this.RegularFont, color, sc, 0, this.Alignment);
|
||||||
base.Draw(time, batch, alpha, blendState, samplerState, matrix);
|
base.Draw(time, batch, alpha, blendState, samplerState, matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +164,7 @@ namespace MLEM.Ui.Elements {
|
||||||
protected virtual void ParseText(Vector2 size) {
|
protected virtual void ParseText(Vector2 size) {
|
||||||
if (this.TokenizedText == null) {
|
if (this.TokenizedText == null) {
|
||||||
// tokenize the text
|
// tokenize the text
|
||||||
this.TokenizedText = this.System.TextFormatter.Tokenize(this.RegularFont, this.Text);
|
this.TokenizedText = this.System.TextFormatter.Tokenize(this.RegularFont, this.Text, this.Alignment);
|
||||||
|
|
||||||
// add links to the paragraph
|
// add links to the paragraph
|
||||||
this.RemoveChildren(c => c is Link);
|
this.RemoveChildren(c => c is Link);
|
||||||
|
@ -162,9 +175,9 @@ namespace MLEM.Ui.Elements {
|
||||||
var width = size.X - this.ScaledPadding.Width;
|
var width = size.X - this.ScaledPadding.Width;
|
||||||
var scale = this.TextScale * this.TextScaleMultiplier * this.Scale;
|
var scale = this.TextScale * this.TextScaleMultiplier * this.Scale;
|
||||||
if (this.TruncateIfLong) {
|
if (this.TruncateIfLong) {
|
||||||
this.TokenizedText.Truncate(this.RegularFont, width, scale, this.Ellipsis);
|
this.TokenizedText.Truncate(this.RegularFont, width, scale, this.Ellipsis, this.Alignment);
|
||||||
} else {
|
} else {
|
||||||
this.TokenizedText.Split(this.RegularFont, width, scale);
|
this.TokenizedText.Split(this.RegularFont, width, scale, this.Alignment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +186,16 @@ namespace MLEM.Ui.Elements {
|
||||||
this.Text = this.GetTextCallback(this);
|
this.Text = this.GetTextCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float GetAlignmentOffset() {
|
||||||
|
switch (this.Alignment) {
|
||||||
|
case TextAlignment.Center:
|
||||||
|
return this.DisplayArea.Width / 2;
|
||||||
|
case TextAlignment.Right:
|
||||||
|
return this.DisplayArea.Width;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A delegate method used for <see cref="Paragraph.GetTextCallback"/>
|
/// A delegate method used for <see cref="Paragraph.GetTextCallback"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -214,7 +237,7 @@ namespace MLEM.Ui.Elements {
|
||||||
public override void ForceUpdateArea() {
|
public override void ForceUpdateArea() {
|
||||||
// set the position offset and size to the token's first area
|
// set the position offset and size to the token's first area
|
||||||
var area = this.Token.GetArea(Vector2.Zero, this.textScale).First();
|
var area = this.Token.GetArea(Vector2.Zero, this.textScale).First();
|
||||||
this.PositionOffset = area.Location;
|
this.PositionOffset = area.Location + new Vector2(((Paragraph) this.Parent).GetAlignmentOffset() / this.Parent.Scale, 0);
|
||||||
this.Size = area.Size;
|
this.Size = area.Size;
|
||||||
base.ForceUpdateArea();
|
base.ForceUpdateArea();
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,13 +70,13 @@ namespace MLEM.Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
///<inheritdoc cref="SpriteFont.MeasureString(string)"/>
|
///<inheritdoc cref="SpriteFont.MeasureString(string)"/>
|
||||||
public Vector2 MeasureString(string text) {
|
public Vector2 MeasureString(string text, bool ignoreTrailingSpaces = false) {
|
||||||
var size = Vector2.Zero;
|
var size = Vector2.Zero;
|
||||||
if (text.Length <= 0)
|
if (text.Length <= 0)
|
||||||
return size;
|
return size;
|
||||||
var xOffset = 0F;
|
var xOffset = 0F;
|
||||||
foreach (var c in text) {
|
for (var i = 0; i < text.Length; i++) {
|
||||||
switch (c) {
|
switch (text[i]) {
|
||||||
case '\n':
|
case '\n':
|
||||||
xOffset = 0;
|
xOffset = 0;
|
||||||
size.Y += this.LineHeight;
|
size.Y += this.LineHeight;
|
||||||
|
@ -90,8 +90,16 @@ namespace MLEM.Font {
|
||||||
case Zwsp:
|
case Zwsp:
|
||||||
// don't add width for a zero-width space
|
// don't add width for a zero-width space
|
||||||
break;
|
break;
|
||||||
|
case ' ':
|
||||||
|
if (ignoreTrailingSpaces && IsTrailingSpace(text, i)) {
|
||||||
|
// if this is a trailing space, we can skip remaining spaces too
|
||||||
|
i = text.Length - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
xOffset += this.MeasureChar(' ').X;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
xOffset += this.MeasureChar(c).X;
|
xOffset += this.MeasureChar(text[i]).X;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// increase x size if this line is the longest
|
// increase x size if this line is the longest
|
||||||
|
@ -186,5 +194,13 @@ namespace MLEM.Font {
|
||||||
return ret.ToString();
|
return ret.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsTrailingSpace(string s, int index) {
|
||||||
|
for (var i = index + 1; i < s.Length; i++) {
|
||||||
|
if (s[i] != ' ')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
22
MLEM/Formatting/TextAlignment.cs
Normal file
22
MLEM/Formatting/TextAlignment.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace MLEM.Formatting {
|
||||||
|
/// <summary>
|
||||||
|
/// An enumeration that represents a set of alignment options for <see cref="TokenizedString"/> objects and MLEM.Ui paragraphs.
|
||||||
|
/// </summary>
|
||||||
|
public enum TextAlignment {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Left alignment, which is also the default value
|
||||||
|
/// </summary>
|
||||||
|
Left,
|
||||||
|
/// <summary>
|
||||||
|
/// Center alignment
|
||||||
|
/// </summary>
|
||||||
|
Center,
|
||||||
|
/// <summary>
|
||||||
|
/// Right alignment.
|
||||||
|
/// In this alignment option, trailing spaces are ignored to ensure that visual alignment is consistent.
|
||||||
|
/// </summary>
|
||||||
|
Right
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,8 +67,9 @@ namespace MLEM.Formatting {
|
||||||
/// </summary>
|
/// </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="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>
|
/// <param name="s">The string to tokenize</param>
|
||||||
|
/// <param name="alignment">The text alignment that should be used. Note that this alignment needs to be the same that will later be used for splitting, measuring and/or drawing.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public TokenizedString Tokenize(GenericFont font, string s) {
|
public TokenizedString Tokenize(GenericFont font, string s, TextAlignment alignment = TextAlignment.Left) {
|
||||||
// resolve macros
|
// resolve macros
|
||||||
s = this.ResolveMacros(s);
|
s = this.ResolveMacros(s);
|
||||||
var tokens = new List<Token>();
|
var tokens = new List<Token>();
|
||||||
|
@ -101,7 +102,7 @@ namespace MLEM.Formatting {
|
||||||
codes.RemoveAll(c => c.EndsHere(next));
|
codes.RemoveAll(c => c.EndsHere(next));
|
||||||
codes.Add(next);
|
codes.Add(next);
|
||||||
}
|
}
|
||||||
return new TokenizedString(font, s, StripFormatting(font, s, tokens.SelectMany(t => t.AppliedCodes)), tokens.ToArray());
|
return new TokenizedString(font, alignment, s, StripFormatting(font, s, tokens.SelectMany(t => t.AppliedCodes)), tokens.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -33,6 +33,10 @@ namespace MLEM.Formatting {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string DisplayString => this.ModifiedSubstring ?? this.Substring;
|
public string DisplayString => this.ModifiedSubstring ?? this.Substring;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// The <see cref="DisplayString"/>, but split at newline characters
|
||||||
|
/// </summary>
|
||||||
|
public string[] SplitDisplayString { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
/// The substring that this token contains, without the formatting codes removed.
|
/// The substring that this token contains, without the formatting codes removed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly string RawSubstring;
|
public readonly string RawSubstring;
|
||||||
|
|
|
@ -38,13 +38,13 @@ namespace MLEM.Formatting {
|
||||||
public readonly Code[] AllCodes;
|
public readonly Code[] AllCodes;
|
||||||
private string modifiedString;
|
private string modifiedString;
|
||||||
|
|
||||||
internal TokenizedString(GenericFont font, string rawString, string strg, Token[] tokens) {
|
internal TokenizedString(GenericFont font, TextAlignment alignment, string rawString, string strg, Token[] tokens) {
|
||||||
this.RawString = rawString;
|
this.RawString = rawString;
|
||||||
this.String = strg;
|
this.String = strg;
|
||||||
this.Tokens = tokens;
|
this.Tokens = tokens;
|
||||||
// since a code can be present in multiple tokens, we use Distinct here
|
// since a code can be present in multiple tokens, we use Distinct here
|
||||||
this.AllCodes = tokens.SelectMany(t => t.AppliedCodes).Distinct().ToArray();
|
this.AllCodes = tokens.SelectMany(t => t.AppliedCodes).Distinct().ToArray();
|
||||||
this.CalculateTokenAreas(font);
|
this.RecalculateTokenData(font, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -55,10 +55,11 @@ namespace MLEM.Formatting {
|
||||||
/// <param name="font">The font to use for width calculations</param>
|
/// <param name="font">The font to use for width calculations</param>
|
||||||
/// <param name="width">The maximum width, in display pixels based on the font and scale</param>
|
/// <param name="width">The maximum width, in display pixels based on the font and scale</param>
|
||||||
/// <param name="scale">The scale to use for width measurements</param>
|
/// <param name="scale">The scale to use for width measurements</param>
|
||||||
public void Split(GenericFont font, float width, float scale) {
|
/// <param name="alignment">The text alignment that should be used for width calculations</param>
|
||||||
|
public void Split(GenericFont font, float width, float scale, TextAlignment alignment = TextAlignment.Left) {
|
||||||
// a split string has the same character count as the input string but with newline characters added
|
// a split string has the same character count as the input string but with newline characters added
|
||||||
this.modifiedString = font.SplitString(this.String, width, scale);
|
this.modifiedString = font.SplitString(this.String, width, scale);
|
||||||
this.StoreModifiedSubstrings(font);
|
this.StoreModifiedSubstrings(font, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -70,12 +71,13 @@ namespace MLEM.Formatting {
|
||||||
/// <param name="width">The maximum width, in display pixels based on the font and scale</param>
|
/// <param name="width">The maximum width, in display pixels based on the font and scale</param>
|
||||||
/// <param name="scale">The scale to use for width measurements</param>
|
/// <param name="scale">The scale to use for width measurements</param>
|
||||||
/// <param name="ellipsis">The characters to add to the end of the string if it is too long</param>
|
/// <param name="ellipsis">The characters to add to the end of the string if it is too long</param>
|
||||||
public void Truncate(GenericFont font, float width, float scale, string ellipsis = "") {
|
/// <param name="alignment">The text alignment that should be used for width calculations</param>
|
||||||
|
public void Truncate(GenericFont font, float width, float scale, string ellipsis = "", TextAlignment alignment = TextAlignment.Left) {
|
||||||
this.modifiedString = font.TruncateString(this.String, width, scale, false, ellipsis);
|
this.modifiedString = font.TruncateString(this.String, width, scale, false, ellipsis);
|
||||||
this.StoreModifiedSubstrings(font);
|
this.StoreModifiedSubstrings(font, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="GenericFont.MeasureString(string)"/>
|
/// <inheritdoc cref="GenericFont.MeasureString(string,bool)"/>
|
||||||
public Vector2 Measure(GenericFont font) {
|
public Vector2 Measure(GenericFont font) {
|
||||||
return font.MeasureString(this.DisplayString);
|
return font.MeasureString(this.DisplayString);
|
||||||
}
|
}
|
||||||
|
@ -102,17 +104,16 @@ namespace MLEM.Formatting {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="GenericFont.DrawString(SpriteBatch,string,Vector2,Color,float,Vector2,float,SpriteEffects,float)"/>
|
/// <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) {
|
public void Draw(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth, TextAlignment alignment = TextAlignment.Left) {
|
||||||
var innerOffset = new Vector2();
|
var innerOffset = new Vector2(this.GetInnerOffsetX(font, 0, 0, scale, alignment), 0);
|
||||||
foreach (var token in this.Tokens) {
|
for (var t = 0; t < this.Tokens.Length; t++) {
|
||||||
|
var token = this.Tokens[t];
|
||||||
var drawFont = token.GetFont(font) ?? font;
|
var drawFont = token.GetFont(font) ?? font;
|
||||||
var drawColor = token.GetColor(color) ?? color;
|
var drawColor = token.GetColor(color) ?? color;
|
||||||
for (var i = 0; i < token.DisplayString.Length; i++) {
|
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
|
||||||
var c = token.DisplayString[i];
|
var line = token.SplitDisplayString[l];
|
||||||
if (c == '\n') {
|
for (var i = 0; i < line.Length; i++) {
|
||||||
innerOffset.X = 0;
|
var c = line[i];
|
||||||
innerOffset.Y += font.LineHeight * scale;
|
|
||||||
}
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
token.DrawSelf(time, batch, pos + innerOffset, font, color, scale, depth);
|
token.DrawSelf(time, batch, pos + innerOffset, font, color, scale, depth);
|
||||||
|
|
||||||
|
@ -120,16 +121,20 @@ namespace MLEM.Formatting {
|
||||||
token.DrawCharacter(time, batch, c, cString, i, pos + innerOffset, drawFont, drawColor, scale, depth);
|
token.DrawCharacter(time, batch, c, cString, i, pos + innerOffset, drawFont, drawColor, scale, depth);
|
||||||
innerOffset.X += font.MeasureString(cString).X * scale;
|
innerOffset.X += font.MeasureString(cString).X * scale;
|
||||||
}
|
}
|
||||||
|
// only split at a new line, not between tokens!
|
||||||
|
if (l < token.SplitDisplayString.Length - 1) {
|
||||||
|
innerOffset.X = this.GetInnerOffsetX(font, t, l + 1, scale, alignment);
|
||||||
|
innerOffset.Y += font.LineHeight * scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StoreModifiedSubstrings(GenericFont font) {
|
private void StoreModifiedSubstrings(GenericFont font, TextAlignment alignment) {
|
||||||
// skip substring logic for unformatted text
|
|
||||||
if (this.Tokens.Length == 1) {
|
if (this.Tokens.Length == 1) {
|
||||||
|
// skip substring logic for unformatted text
|
||||||
this.Tokens[0].ModifiedSubstring = this.modifiedString;
|
this.Tokens[0].ModifiedSubstring = this.modifiedString;
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
// this is basically a substring function that ignores added newlines for indexing
|
// this is basically a substring function that ignores added newlines for indexing
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var currToken = 0;
|
var currToken = 0;
|
||||||
|
@ -156,23 +161,59 @@ namespace MLEM.Formatting {
|
||||||
this.Tokens[currToken++].ModifiedSubstring = ret.ToString();
|
this.Tokens[currToken++].ModifiedSubstring = ret.ToString();
|
||||||
while (currToken < this.Tokens.Length)
|
while (currToken < this.Tokens.Length)
|
||||||
this.Tokens[currToken++].ModifiedSubstring = string.Empty;
|
this.Tokens[currToken++].ModifiedSubstring = string.Empty;
|
||||||
|
|
||||||
this.CalculateTokenAreas(font);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateTokenAreas(GenericFont font) {
|
this.RecalculateTokenData(font, alignment);
|
||||||
var innerOffset = new Vector2();
|
}
|
||||||
foreach (var token in this.Tokens) {
|
|
||||||
|
private float GetInnerOffsetX(GenericFont font, int tokenIndex, int lineIndex, float scale, TextAlignment alignment) {
|
||||||
|
if (alignment > TextAlignment.Left) {
|
||||||
|
var rest = this.GetRestOfLineLength(font, tokenIndex, lineIndex) * scale;
|
||||||
|
if (alignment == TextAlignment.Center)
|
||||||
|
rest /= 2;
|
||||||
|
return -rest;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetRestOfLineLength(GenericFont font, int tokenIndex, int lineIndex) {
|
||||||
|
var token = this.Tokens[tokenIndex];
|
||||||
|
var ret = font.MeasureString(token.SplitDisplayString[lineIndex], true).X;
|
||||||
|
if (lineIndex >= token.SplitDisplayString.Length - 1) {
|
||||||
|
// the line ends somewhere in or after the next token
|
||||||
|
for (var i = tokenIndex + 1; i < this.Tokens.Length; i++) {
|
||||||
|
var other = this.Tokens[i];
|
||||||
|
if (other.SplitDisplayString.Length > 1) {
|
||||||
|
// the line ends in this token
|
||||||
|
ret += font.MeasureString(other.SplitDisplayString[0]).X;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// the line doesn't end in this token, so add it fully
|
||||||
|
ret += font.MeasureString(other.DisplayString).X;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecalculateTokenData(GenericFont font, TextAlignment alignment) {
|
||||||
|
// split display strings
|
||||||
|
foreach (var token in this.Tokens)
|
||||||
|
token.SplitDisplayString = token.DisplayString.Split('\n');
|
||||||
|
|
||||||
|
// token areas
|
||||||
|
var innerOffset = new Vector2(this.GetInnerOffsetX(font, 0, 0, 1, alignment), 0);
|
||||||
|
for (var t = 0; t < this.Tokens.Length; t++) {
|
||||||
|
var token = this.Tokens[t];
|
||||||
var area = new List<RectangleF>();
|
var area = new List<RectangleF>();
|
||||||
var split = token.DisplayString.Split('\n');
|
for (var l = 0; l < token.SplitDisplayString.Length; l++) {
|
||||||
for (var i = 0; i < split.Length; i++) {
|
var size = font.MeasureString(token.SplitDisplayString[l]);
|
||||||
var size = font.MeasureString(split[i]);
|
|
||||||
var rect = new RectangleF(innerOffset, size);
|
var rect = new RectangleF(innerOffset, size);
|
||||||
if (!rect.IsEmpty)
|
if (!rect.IsEmpty)
|
||||||
area.Add(rect);
|
area.Add(rect);
|
||||||
|
|
||||||
if (i < split.Length - 1) {
|
if (l < token.SplitDisplayString.Length - 1) {
|
||||||
innerOffset.X = 0;
|
innerOffset.X = this.GetInnerOffsetX(font, t, l + 1, 1, alignment);
|
||||||
innerOffset.Y += font.LineHeight;
|
innerOffset.Y += font.LineHeight;
|
||||||
} else {
|
} else {
|
||||||
innerOffset.X += size.X;
|
innerOffset.X += size.X;
|
||||||
|
|
Loading…
Reference in a new issue