using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Text; using System.Xml.Linq; namespace Barotrauma.Items.Components { partial class ItemLabel : ItemComponent, IDrawableComponent { private GUITextBlock textBlock; private Color textColor; private float scrollAmount; private string scrollingText; private float scrollPadding; private int scrollIndex; private bool needsScrolling; private float[] charWidths; [Serialize("0,0,0,0", true)] public Vector4 Padding { get { return TextBlock.Padding; } set { TextBlock.Padding = value; } } private string text; [Serialize("", true), Editable(100)] public string Text { get { return text; } set { if (value == text || item.Rect.Width < 5) return; if (TextBlock.Rect.Width != item.Rect.Width || textBlock.Rect.Height != item.Rect.Height) { textBlock = null; } text = value; TextBlock.Text = value; SetScrollingText(); } } [Editable, Serialize("0.0,0.0,0.0,1.0", true)] public Color TextColor { get { return textColor; } set { if (textBlock != null) textBlock.TextColor = value; textColor = value; } } [Editable(0.0f, 10.0f), Serialize(1.0f, true)] public float TextScale { get { return textBlock == null ? 1.0f : textBlock.TextScale; } set { if (textBlock != null) textBlock.TextScale = MathHelper.Clamp(value, 0.1f, 10.0f); } } private bool scrollable; [Serialize(false, true)] public bool Scrollable { get { return scrollable; } set { scrollable = value; IsActive = value; TextBlock.Wrap = !scrollable; TextBlock.TextAlignment = scrollable ? Alignment.CenterLeft : Alignment.Center; } } [Serialize(20.0f, true)] public float ScrollSpeed { get; set; } private GUITextBlock TextBlock { get { if (textBlock == null) { textBlock = new GUITextBlock(new RectTransform(item.Rect.Size), "", textColor: textColor, font: GUI.SmallFont, textAlignment: Alignment.Center, wrap: true, style: null) { TextDepth = item.SpriteDepth - 0.0001f, TextScale = TextScale }; } return textBlock; } } public ItemLabel(Item item, XElement element) : base(item, element) { } private void SetScrollingText() { if (!scrollable) return; float totalWidth = textBlock.Font.MeasureString(text).X; float textAreaWidth = Math.Max(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z, 0); if (totalWidth >= textAreaWidth) { //add enough spaces to fill the rect //(so the text can scroll entirely out of view before we reset it back to start) needsScrolling = true; float spaceWidth = textBlock.Font.MeasureChar(' ').X; scrollingText = new string(' ', (int)Math.Ceiling(textAreaWidth / spaceWidth)) + text; } else { //whole text can fit in the textblock, no need to scroll needsScrolling = false; scrollingText = text; scrollAmount = 0.0f; scrollIndex = 0; return; } //calculate character widths scrollPadding = 0; charWidths = new float[scrollingText.Length]; for (int i = 0; i < scrollingText.Length; i++) { float charWidth = TextBlock.Font.MeasureChar(scrollingText[i]).X; scrollPadding = Math.Max(charWidth, scrollPadding); charWidths[i] = charWidth; } scrollIndex = MathHelper.Clamp(scrollIndex, 0, text.Length); } public override void Update(float deltaTime, Camera cam) { if (!scrollable) return; if (scrollingText == null) { SetScrollingText(); } if (!needsScrolling) return; scrollAmount -= deltaTime * ScrollSpeed; float currLength = 0; StringBuilder sb = new StringBuilder(); float textAreaWidth = textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z; for (int i = scrollIndex; i < scrollingText.Length; i++) { //first character is out of view -> skip to next character if (i == scrollIndex && scrollAmount < -charWidths[i]) { scrollIndex++; scrollAmount = 0; if (scrollIndex >= scrollingText.Length) //reached the last character, reset { scrollIndex = 0; break; } continue; } //reached the right edge, stop adding more character if (scrollAmount + (currLength + charWidths[i] + scrollPadding) >= textAreaWidth) { break; } else { currLength += charWidths[i]; sb.Append(scrollingText[i]); } } TextBlock.Text = sb.ToString(); } public void Draw(SpriteBatch spriteBatch, bool editing = false) { var drawPos = new Vector2( item.DrawPosition.X - item.Rect.Width / 2.0f, -(item.DrawPosition.Y + item.Rect.Height / 2.0f)); Rectangle worldRect = item.WorldRect; if (worldRect.X > Screen.Selected.Cam.WorldView.Right || worldRect.Right < Screen.Selected.Cam.WorldView.X || worldRect.Y < Screen.Selected.Cam.WorldView.Y - Screen.Selected.Cam.WorldView.Height || worldRect.Y - worldRect.Height > Screen.Selected.Cam.WorldView.Y) { return; } textBlock.TextDepth = item.SpriteDepth - 0.0001f; textBlock.TextOffset = drawPos - textBlock.Rect.Location.ToVector2() + new Vector2(scrollAmount + scrollPadding, 0.0f); textBlock.DrawManually(spriteBatch); } } }