using OpenTK; using RecrownedAthenaeum.Graphics.Render; using System; using System.Text; namespace RecrownedAthenaeum.Graphics.UI.Modular.Modules { /// /// Represents text for the UI. /// public class Text : UIModule { private SpriteFont font; private float scale = 1f; private Vector2 position; private string originalText; private string displayedText; private Vector2 modifiedTextSize; /// /// Centers the text int bounds. /// public bool centered; /// /// Whether or not to try and wrap text automatically. Meaning will check and attempt to wrap every update. /// public bool autoWrap; /// /// Whether or not to automatically scale the text every update. Happens after auto wrap if enabled. /// public bool autoScale; /// /// Should this use ellipses? Will perform this operation before auto wrapping or auto scalling. /// public bool useEllipses; /// /// The text to use for the ellipsis. /// public string ellipsis = "..."; private string ModifiedText { get { return displayedText; } set { displayedText = value; modifiedTextSize = font.MeasureString(value); } } /// /// The string to be displayed. /// public string Content { get { return originalText; } set { originalText = value; if (value == null) value = ellipsis; modifiedTextSize = font.MeasureString(value); displayedText = value; } } /// /// Creates a UI text object. /// /// The font to use. /// The string for the text. public Text(SpriteFont font, string content = null) { this.font = font ?? throw new ArgumentNullException("Font cannot be null."); Content = content; } /// /// Updates the positioning and attempts to perform any operations that were marked automatic. /// /// The game time. public override void Update(GameTime gameTime) { position.X = X; position.Y = Y; if (useEllipses) AttemptToApplyEllipsis(); if (autoWrap) AttemptToWrapText(); if (autoScale) AttemptToScaleFont(); if (centered) Center(); base.Update(gameTime); } /// /// Draws the text. /// /// Batch to use. public override void Draw(ConsistentSpriteBatch batch) { batch.DrawString(font, Content, position, color, 0f, default(Vector2), scale, SpriteEffects.None, 0f); base.Draw(batch); } /// /// Attempts to apply ellipsis. Checks of nessecary. /// public void AttemptToApplyEllipsis() { if (modifiedTextSize.X * scale > Width && ModifiedText.Length > ellipsis.Length + 1) { RemoveLineBreaks(); StringBuilder stringBuilder = new StringBuilder(ModifiedText); do { stringBuilder.Remove(stringBuilder.Length, ellipsis.Length - 1); stringBuilder.Insert(stringBuilder.Length, ellipsis); } while (font.MeasureString(stringBuilder).X * scale > Width); ModifiedText = stringBuilder.ToString(); } } /// /// Attempts to scale the font. Checks if nessecary. /// public void AttemptToScaleFont() { if (Width < Height) { if (Math.Round(modifiedTextSize.X * scale ) != Width) { scale = Width / modifiedTextSize.X; } } else { if (Math.Round(modifiedTextSize.Y * scale ) != Height) { scale = Height / (modifiedTextSize.Y); } } } /// /// Removes line breaks. /// public void RemoveLineBreaks() { ModifiedText = ModifiedText.Replace("\n", " "); } /// /// Resets to original text. /// public void ResetToOriginalText() { Content = originalText; } /// /// Attempts to wrap text. Checks if nessecary. /// /// If true, will first unwrap text, and the wrap again. This occurs before nessecity check. public void AttemptToWrapText(bool unwrap = false) { if (unwrap) RemoveLineBreaks(); if (modifiedTextSize.X * scale > Width) { ModifiedText = ModifiedText.Replace("\n", " "); string[] words = ModifiedText.Split(' '); StringBuilder stringBuilder = new StringBuilder(); float currentScaledLineWidth = 0f; for (int w = 0; w < words.Length; w++) { string word = words[w]; float scaledWidth = font.MeasureString(word).X * scale; if (currentScaledLineWidth + scaledWidth <= Width) { stringBuilder.Append(word); currentScaledLineWidth += scaledWidth; } else { stringBuilder.AppendLine(); currentScaledLineWidth = 0; } } ModifiedText = stringBuilder.ToString(); } } private bool Center() { Vector2 textSize = new Vector2(modifiedTextSize.X * scale, modifiedTextSize.Y * scale); if (textSize.X <= Width) { position.X = X + (Width - textSize.X) / 2f; } else { return false; } if (textSize.Y <= Height) { position.Y = Y + (Height - textSize.Y) / 2f; } else { return false; } return true; } } }