mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-24 13:38:34 +01:00
Compare commits
2 commits
f94d471365
...
d385581c25
Author | SHA1 | Date | |
---|---|---|---|
d385581c25 | |||
e916ddb7a8 |
8 changed files with 123 additions and 44 deletions
|
@ -60,6 +60,17 @@ namespace MLEM.Ui.Elements {
|
||||||
this.CanBeSelected = !value;
|
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>
|
/// <summary>
|
||||||
/// Creates a new button with the given settings
|
/// 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>
|
/// <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) {
|
public Button(Anchor anchor, Vector2 size, string text = null, string tooltipText = null, float tooltipWidth = 50) : base(anchor, size) {
|
||||||
if (text != null) {
|
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);
|
this.AddChild(this.Text);
|
||||||
}
|
}
|
||||||
if (tooltipText != null)
|
if (tooltipText != null)
|
||||||
|
|
|
@ -64,6 +64,16 @@ namespace MLEM.Ui.Elements {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutoAdjustWidth;
|
public bool AutoAdjustWidth;
|
||||||
/// <summary>
|
/// <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.
|
/// 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.
|
/// Use this event for setting this paragraph's text if it changes frequently.
|
||||||
/// </summary>
|
/// </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)))
|
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.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() {
|
private void QueryTextCallback() {
|
||||||
|
|
17
MLEM/Formatting/Codes/ResetFormattingCode.cs
Normal file
17
MLEM/Formatting/Codes/ResetFormattingCode.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace MLEM.Formatting.Codes {
|
||||||
|
/// <inheritdoc />
|
||||||
|
public class ResetFormattingCode : Code {
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ResetFormattingCode(Match match, Regex regex) : base(match, regex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool EndsHere(Code other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,13 +6,13 @@ using MLEM.Font;
|
||||||
|
|
||||||
namespace MLEM.Formatting.Codes {
|
namespace MLEM.Formatting.Codes {
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class ShadowCode : FontCode {
|
public class ShadowCode : Code {
|
||||||
|
|
||||||
private readonly Color color;
|
private readonly Color color;
|
||||||
private readonly Vector2 offset;
|
private readonly Vector2 offset;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ShadowCode(Match match, Regex regex, Color color, Vector2 offset) : base(match, regex, null) {
|
public ShadowCode(Match match, Regex regex, Color color, Vector2 offset) : base(match, regex) {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,10 @@ namespace MLEM.Formatting.Codes {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool EndsHere(Code other) {
|
||||||
|
return base.EndsHere(other) || other is ResetFormattingCode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -29,5 +29,10 @@ namespace MLEM.Formatting.Codes {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool EndsHere(Code other) {
|
||||||
|
return base.EndsHere(other) || other is ResetFormattingCode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -38,7 +38,8 @@ namespace MLEM.Formatting {
|
||||||
m.Groups[1].Success ? ColorHelper.FromHexString(m.Groups[1].Value) : Color.Black,
|
m.Groups[1].Success ? ColorHelper.FromHexString(m.Groups[1].Value) : Color.Black,
|
||||||
new Vector2(float.TryParse(m.Groups[2].Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var offset) ? offset : 2)));
|
new Vector2(float.TryParse(m.Groups[2].Value, NumberStyles.Number, CultureInfo.InvariantCulture, out var offset) ? offset : 2)));
|
||||||
this.Codes.Add(new Regex("<u>"), (f, m, r) => new UnderlineCode(m, r, 1 / 16F, 0.85F));
|
this.Codes.Add(new Regex("<u>"), (f, m, r) => new UnderlineCode(m, r, 1 / 16F, 0.85F));
|
||||||
this.Codes.Add(new Regex("</(b|i|s|u|l)>"), (f, m, r) => new FontCode(m, r, null));
|
this.Codes.Add(new Regex("</(s|u|l)>"), (f, m, r) => new ResetFormattingCode(m, r));
|
||||||
|
this.Codes.Add(new Regex("</(b|i)>"), (f, m, r) => new FontCode(m, r, null));
|
||||||
|
|
||||||
// color codes
|
// color codes
|
||||||
foreach (var c in typeof(Color).GetProperties()) {
|
foreach (var c in typeof(Color).GetProperties()) {
|
||||||
|
|
|
@ -29,15 +29,15 @@ namespace MLEM.Formatting {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly string Substring;
|
public readonly string Substring;
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public string DisplayString => this.SplitSubstring ?? this.Substring;
|
public string DisplayString => this.ModifiedSubstring ?? this.Substring;
|
||||||
/// <summary>
|
/// <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;
|
||||||
internal RectangleF[] Area;
|
internal RectangleF[] Area;
|
||||||
internal string SplitSubstring;
|
internal string ModifiedSubstring;
|
||||||
|
|
||||||
internal Token(Code[] appliedCodes, int index, int rawIndex, string substring, string rawSubstring) {
|
internal Token(Code[] appliedCodes, int index, int rawIndex, string substring, string rawSubstring) {
|
||||||
this.AppliedCodes = appliedCodes;
|
this.AppliedCodes = appliedCodes;
|
||||||
|
|
|
@ -24,9 +24,9 @@ namespace MLEM.Formatting {
|
||||||
public readonly string String;
|
public readonly string String;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The string that is actually displayed by this tokenized string.
|
/// 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>
|
/// </summary>
|
||||||
public string DisplayString => this.splitString ?? this.String;
|
public string DisplayString => this.modifiedString ?? this.String;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The tokens that this tokenized string contains.
|
/// The tokens that this tokenized string contains.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -36,7 +36,7 @@ namespace MLEM.Formatting {
|
||||||
/// Note that, to get a formatting code for a certain token, use <see cref="Token.AppliedCodes"/>
|
/// Note that, to get a formatting code for a certain token, use <see cref="Token.AppliedCodes"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Code[] AllCodes;
|
public readonly Code[] AllCodes;
|
||||||
private string splitString;
|
private string modifiedString;
|
||||||
|
|
||||||
internal TokenizedString(GenericFont font, string rawString, string strg, Token[] tokens) {
|
internal TokenizedString(GenericFont font, string rawString, string strg, Token[] tokens) {
|
||||||
this.RawString = rawString;
|
this.RawString = rawString;
|
||||||
|
@ -49,44 +49,30 @@ namespace MLEM.Formatting {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Splits this tokenized string, inserting newline characters if the width of the string is bigger than the maximum width.
|
/// 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"/>
|
/// <seealso cref="GenericFont.SplitString"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <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</param>
|
/// <param name="width">The maximum width, in display pixels based on the font and scale</param>
|
||||||
/// <param name="scale">The scale to use fr width calculations</param>
|
/// <param name="scale">The scale to use for width measurements</param>
|
||||||
public void Split(GenericFont font, float width, float scale) {
|
public void Split(GenericFont font, float width, float scale) {
|
||||||
// a split string has the same character count as the input string
|
// a split string has the same character count as the input string but with newline characters added
|
||||||
// but with newline characters added
|
this.modifiedString = font.SplitString(this.String, width, scale);
|
||||||
this.splitString = font.SplitString(this.String, width, scale);
|
this.StoreModifiedSubstrings(font);
|
||||||
// skip splitting logic for unformatted text
|
}
|
||||||
if (this.Tokens.Length == 1) {
|
|
||||||
this.Tokens[0].SplitSubstring = this.splitString;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is basically a substring function that ignores newlines for indexing
|
/// <summary>
|
||||||
var index = 0;
|
/// Truncates this tokenized string, removing any additional characters that exceed the length from the displayed string.
|
||||||
var currToken = 0;
|
/// 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"/>.
|
||||||
var splitIndex = 0;
|
/// <seealso cref="GenericFont.TruncateString"/>
|
||||||
var ret = new StringBuilder();
|
/// </summary>
|
||||||
while (splitIndex < this.splitString.Length) {
|
/// <param name="font">The font to use for width calculations</param>
|
||||||
var token = this.Tokens[currToken];
|
/// <param name="width">The maximum width, in display pixels based on the font and scale</param>
|
||||||
if (token.Substring.Length > 0) {
|
/// <param name="scale">The scale to use for width measurements</param>
|
||||||
ret.Append(this.splitString[splitIndex]);
|
/// <param name="ellipsis">The characters to add to the end of the string if it is too long</param>
|
||||||
// if the current char is not an added newline, we simulate length increase
|
public void Truncate(GenericFont font, float width, float scale, string ellipsis = "") {
|
||||||
if (this.splitString[splitIndex] != '\n' || this.String[index] == '\n')
|
this.modifiedString = font.TruncateString(this.String, width, scale, false, ellipsis);
|
||||||
index++;
|
this.StoreModifiedSubstrings(font);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="GenericFont.MeasureString(string)"/>
|
/// <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) {
|
private void CalculateTokenAreas(GenericFont font) {
|
||||||
var innerOffset = new Vector2();
|
var innerOffset = new Vector2();
|
||||||
foreach (var token in this.Tokens) {
|
foreach (var token in this.Tokens) {
|
||||||
|
|
Loading…
Reference in a new issue