2020-05-15 00:34:04 +02:00
|
|
|
using System;
|
2020-05-17 00:10:29 +02:00
|
|
|
using System.Collections.Generic;
|
2020-05-15 00:34:04 +02:00
|
|
|
using System.Linq;
|
2020-05-15 13:16:03 +02:00
|
|
|
using System.Text;
|
2020-05-15 00:34:04 +02:00
|
|
|
using Microsoft.Xna.Framework;
|
|
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
using MLEM.Font;
|
2020-05-15 14:22:33 +02:00
|
|
|
using MLEM.Formatting.Codes;
|
|
|
|
using MLEM.Misc;
|
2020-05-15 00:34:04 +02:00
|
|
|
|
|
|
|
namespace MLEM.Formatting {
|
2020-05-15 14:22:33 +02:00
|
|
|
public class TokenizedString : GenericDataHolder {
|
2020-05-15 00:34:04 +02:00
|
|
|
|
|
|
|
public readonly string RawString;
|
2020-05-17 00:10:29 +02:00
|
|
|
public readonly string String;
|
|
|
|
public string DisplayString => this.splitString ?? this.String;
|
2020-05-15 00:34:04 +02:00
|
|
|
public readonly Token[] Tokens;
|
2020-05-15 14:22:33 +02:00
|
|
|
public readonly Code[] AllCodes;
|
2020-05-17 00:10:29 +02:00
|
|
|
private string splitString;
|
2020-05-15 00:34:04 +02:00
|
|
|
|
2020-05-17 00:10:29 +02:00
|
|
|
public TokenizedString(GenericFont font, string rawString, string strg, Token[] tokens) {
|
2020-05-15 00:34:04 +02:00
|
|
|
this.RawString = rawString;
|
|
|
|
this.String = strg;
|
|
|
|
this.Tokens = tokens;
|
2020-05-15 14:22:33 +02:00
|
|
|
// since a code can be present in multiple tokens, we use Distinct here
|
|
|
|
this.AllCodes = tokens.SelectMany(t => t.AppliedCodes).Distinct().ToArray();
|
2020-05-17 00:10:29 +02:00
|
|
|
this.CalculateTokenAreas(font);
|
2020-05-15 00:34:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Split(GenericFont font, float width, float scale) {
|
2020-05-15 13:16:03 +02:00
|
|
|
// a split string has the same character count as the input string
|
|
|
|
// but with newline characters added
|
2020-05-17 00:10:29 +02:00
|
|
|
this.splitString = font.SplitString(this.String, width, scale);
|
2020-05-15 19:55:59 +02:00
|
|
|
// skip splitting logic for unformatted text
|
|
|
|
if (this.Tokens.Length == 1) {
|
2020-05-17 00:10:29 +02:00
|
|
|
this.Tokens[0].SplitSubstring = this.splitString;
|
2020-05-15 19:55:59 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-05-15 13:16:03 +02:00
|
|
|
foreach (var token in this.Tokens) {
|
|
|
|
var index = 0;
|
|
|
|
var length = 0;
|
|
|
|
var ret = new StringBuilder();
|
|
|
|
// this is basically a substring function that ignores newlines for indexing
|
2020-05-17 00:10:29 +02:00
|
|
|
for (var i = 0; i < this.splitString.Length; i++) {
|
2020-05-15 13:16:03 +02:00
|
|
|
// if we're within the bounds of the token's substring, append to the new substring
|
|
|
|
if (index >= token.Index && length < token.Substring.Length)
|
2020-05-17 00:10:29 +02:00
|
|
|
ret.Append(this.splitString[i]);
|
2020-05-15 13:16:03 +02:00
|
|
|
// if the current char is not a newline, we simulate length increase
|
2020-05-17 00:10:29 +02:00
|
|
|
if (this.splitString[i] != '\n') {
|
2020-05-15 13:16:03 +02:00
|
|
|
if (index >= token.Index)
|
|
|
|
length++;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
}
|
2020-05-17 00:10:29 +02:00
|
|
|
token.SplitSubstring = ret.ToString();
|
2020-05-15 13:16:03 +02:00
|
|
|
}
|
2020-05-17 00:10:29 +02:00
|
|
|
this.CalculateTokenAreas(font);
|
2020-05-15 00:34:04 +02:00
|
|
|
}
|
|
|
|
|
2020-05-15 19:55:59 +02:00
|
|
|
public Vector2 Measure(GenericFont font) {
|
2020-05-17 00:10:29 +02:00
|
|
|
return font.MeasureString(this.DisplayString);
|
2020-05-15 19:55:59 +02:00
|
|
|
}
|
|
|
|
|
2020-05-15 14:22:33 +02:00
|
|
|
public void Update(GameTime time) {
|
|
|
|
foreach (var code in this.AllCodes)
|
|
|
|
code.Update(time);
|
|
|
|
}
|
|
|
|
|
2020-05-17 00:10:29 +02:00
|
|
|
public Token GetTokenUnderPos(Vector2 stringPos, Vector2 target, float scale) {
|
|
|
|
return this.Tokens.FirstOrDefault(t => t.GetArea(stringPos, scale).Any(r => r.Contains(target)));
|
2020-05-15 22:15:24 +02:00
|
|
|
}
|
|
|
|
|
2020-05-15 00:34:04 +02:00
|
|
|
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) {
|
2020-05-17 00:10:29 +02:00
|
|
|
var drawFont = token.GetFont(font) ?? font;
|
|
|
|
var drawColor = token.GetColor(color) ?? color;
|
|
|
|
for (var i = 0; i < token.DisplayString.Length; i++) {
|
|
|
|
var c = token.DisplayString[i];
|
2020-05-15 00:34:04 +02:00
|
|
|
if (c == '\n') {
|
|
|
|
innerOffset.X = 0;
|
|
|
|
innerOffset.Y += font.LineHeight * scale;
|
|
|
|
}
|
2020-05-15 19:55:59 +02:00
|
|
|
if (i == 0)
|
|
|
|
token.DrawSelf(time, batch, pos + innerOffset, font, color, scale, depth);
|
2020-05-15 00:34:04 +02:00
|
|
|
|
|
|
|
var cString = c.ToString();
|
2020-05-15 14:22:33 +02:00
|
|
|
token.DrawCharacter(time, batch, c, cString, i, pos + innerOffset, drawFont, drawColor, scale, depth);
|
2020-05-15 00:34:04 +02:00
|
|
|
innerOffset.X += font.MeasureString(cString).X * scale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 00:10:29 +02:00
|
|
|
private void CalculateTokenAreas(GenericFont font) {
|
|
|
|
var innerOffset = new Vector2();
|
|
|
|
foreach (var token in this.Tokens) {
|
|
|
|
var area = new List<RectangleF>();
|
|
|
|
var split = token.DisplayString.Split('\n');
|
|
|
|
for (var i = 0; i < split.Length; i++) {
|
|
|
|
var size = font.MeasureString(split[i]);
|
2020-05-19 21:52:29 +02:00
|
|
|
var rect = new RectangleF(innerOffset, size);
|
|
|
|
if (!rect.IsEmpty)
|
|
|
|
area.Add(rect);
|
2020-05-17 00:10:29 +02:00
|
|
|
|
|
|
|
if (i < split.Length - 1) {
|
|
|
|
innerOffset.X = 0;
|
|
|
|
innerOffset.Y += font.LineHeight;
|
|
|
|
} else {
|
|
|
|
innerOffset.X += size.X;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
token.Area = area.ToArray();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-15 00:34:04 +02:00
|
|
|
}
|
|
|
|
}
|