1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-25 22:18:34 +01:00

added formatted string truncation to tokenized strings and ui paragraphs

This commit is contained in:
Ell 2021-05-18 16:47:38 +02:00
parent e916ddb7a8
commit d385581c25
4 changed files with 92 additions and 41 deletions

View file

@ -60,6 +60,17 @@ namespace MLEM.Ui.Elements {
this.CanBeSelected = !value;
}
}
/// <summary>
/// Whether this button's <see cref="Text"/> should be truncated if it exceeds this button's width.
/// Defaults to false.
/// </summary>
public bool TruncateTextIfLong {
get => this.Text?.TruncateIfLong ?? false;
set {
if (this.Text != null)
this.Text.TruncateIfLong = value;
}
}
/// <summary>
/// Creates a new button with the given settings
@ -71,7 +82,7 @@ namespace MLEM.Ui.Elements {
/// <param name="tooltipWidth">The width of this button's <see cref="Tooltip"/>, or 50 by default</param>
public Button(Anchor anchor, Vector2 size, string text = null, string tooltipText = null, float tooltipWidth = 50) : base(anchor, size) {
if (text != null) {
this.Text = new Paragraph(Anchor.Center, 1, text, true);
this.Text = new Paragraph(Anchor.Center, 1, text, true) {Padding = new Vector2(1)};
this.AddChild(this.Text);
}
if (tooltipText != null)

View file

@ -64,6 +64,16 @@ namespace MLEM.Ui.Elements {
/// </summary>
public bool AutoAdjustWidth;
/// <summary>
/// Whether this paragraph should be truncated instead of split if the displayed <see cref="Text"/>'s width exceeds the provided width.
/// When the string is truncated, the <see cref="Ellipsis"/> is added to its end.
/// </summary>
public bool TruncateIfLong;
/// <summary>
/// The ellipsis characters to use if <see cref="TruncateIfLong"/> is enabled and the string is truncated.
/// If this is set to an empty string, no ellipsis will be attached to the truncated string.
/// </summary>
public string Ellipsis = "...";
/// <summary>
/// An event that gets called when this paragraph's <see cref="Text"/> is queried.
/// Use this event for setting this paragraph's text if it changes frequently.
/// </summary>
@ -142,7 +152,14 @@ namespace MLEM.Ui.Elements {
foreach (var link in this.TokenizedText.Tokens.Where(t => t.AppliedCodes.Any(c => c is LinkCode)))
this.AddChild(new Link(Anchor.TopLeft, link, this.TextScale * this.TextScaleMultiplier));
}
this.TokenizedText.Split(this.RegularFont, size.X - this.ScaledPadding.Width, this.TextScale * this.TextScaleMultiplier * this.Scale);
var width = size.X - this.ScaledPadding.Width;
var scale = this.TextScale * this.TextScaleMultiplier * this.Scale;
if (this.TruncateIfLong) {
this.TokenizedText.Truncate(this.RegularFont, width, scale, this.Ellipsis);
} else {
this.TokenizedText.Split(this.RegularFont, width, scale);
}
}
private void QueryTextCallback() {

View file

@ -29,15 +29,15 @@ namespace MLEM.Formatting {
/// </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.
/// The string that is displayed by this token. If the tokenized string has been <see cref="TokenizedString.Split"/> or <see cref="TokenizedString.Truncate"/> has been used, this string will contain the newline characters.
/// </summary>
public string DisplayString => this.SplitSubstring ?? this.Substring;
public string DisplayString => this.ModifiedSubstring ?? 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;
internal string ModifiedSubstring;
internal Token(Code[] appliedCodes, int index, int rawIndex, string substring, string rawSubstring) {
this.AppliedCodes = appliedCodes;

View file

@ -24,9 +24,9 @@ namespace MLEM.Formatting {
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.
/// If this string has been <see cref="Split"/> or <see cref="Truncate"/> has been used, this string will contain the newline characters.
/// </summary>
public string DisplayString => this.splitString ?? this.String;
public string DisplayString => this.modifiedString ?? this.String;
/// <summary>
/// The tokens that this tokenized string contains.
/// </summary>
@ -36,7 +36,7 @@ namespace MLEM.Formatting {
/// Note that, to get a formatting code for a certain token, use <see cref="Token.AppliedCodes"/>
/// </summary>
public readonly Code[] AllCodes;
private string splitString;
private string modifiedString;
internal TokenizedString(GenericFont font, string rawString, string strg, Token[] tokens) {
this.RawString = rawString;
@ -49,44 +49,30 @@ namespace MLEM.Formatting {
/// <summary>
/// Splits this tokenized string, inserting newline characters if the width of the string is bigger than the maximum width.
/// Note that a tokenized string can be re-split without losing any of its actual data, as this operation merely modifies the <see cref="DisplayString"/>.
/// <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>
/// <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>
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
this.splitString = font.SplitString(this.String, width, scale);
// skip splitting logic for unformatted text
if (this.Tokens.Length == 1) {
this.Tokens[0].SplitSubstring = this.splitString;
return;
}
// 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.StoreModifiedSubstrings(font);
}
// this is basically a substring function that ignores newlines for indexing
var index = 0;
var currToken = 0;
var splitIndex = 0;
var ret = new StringBuilder();
while (splitIndex < this.splitString.Length) {
var token = this.Tokens[currToken];
if (token.Substring.Length > 0) {
ret.Append(this.splitString[splitIndex]);
// if the current char is not an added newline, we simulate length increase
if (this.splitString[splitIndex] != '\n' || this.String[index] == '\n')
index++;
splitIndex++;
}
// move on to the next token if we reached its end
if (index >= token.Index + token.Substring.Length) {
token.SplitSubstring = ret.ToString();
ret.Clear();
currToken++;
}
}
this.CalculateTokenAreas(font);
/// <summary>
/// Truncates this tokenized string, removing any additional characters that exceed the length from the displayed string.
/// Note that a tokenized string can be re-truncated without losing any of its actual data, as this operation merely modifies the <see cref="DisplayString"/>.
/// <seealso cref="GenericFont.TruncateString"/>
/// </summary>
/// <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="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>
public void Truncate(GenericFont font, float width, float scale, string ellipsis = "") {
this.modifiedString = font.TruncateString(this.String, width, scale, false, ellipsis);
this.StoreModifiedSubstrings(font);
}
/// <inheritdoc cref="GenericFont.MeasureString(string)"/>
@ -137,6 +123,43 @@ namespace MLEM.Formatting {
}
}
private void StoreModifiedSubstrings(GenericFont font) {
// skip substring logic for unformatted text
if (this.Tokens.Length == 1) {
this.Tokens[0].ModifiedSubstring = this.modifiedString;
return;
}
// this is basically a substring function that ignores added newlines for indexing
var index = 0;
var currToken = 0;
var splitIndex = 0;
var ret = new StringBuilder();
while (splitIndex < this.modifiedString.Length) {
var token = this.Tokens[currToken];
if (token.Substring.Length > 0) {
ret.Append(this.modifiedString[splitIndex]);
// if the current char is not an added newline, we simulate length increase
if (this.modifiedString[splitIndex] != '\n' || this.String[index] == '\n')
index++;
splitIndex++;
}
// move on to the next token if we reached its end
if (index >= token.Index + token.Substring.Length) {
token.ModifiedSubstring = ret.ToString();
ret.Clear();
currToken++;
}
}
// set additional token contents beyond our string in case we truncated
if (ret.Length > 0)
this.Tokens[currToken - 1].ModifiedSubstring += ret.ToString();
while (currToken < this.Tokens.Length)
this.Tokens[currToken++].ModifiedSubstring = string.Empty;
this.CalculateTokenAreas(font);
}
private void CalculateTokenAreas(GenericFont font) {
var innerOffset = new Vector2();
foreach (var token in this.Tokens) {