In-line Button Glyphs

From XNAWiki
Jump to: navigation, search


This class can be used to render button glyphs in-line with normal text. To make use of this component you need the button glyphs font found on the XNA website. It can also do text wrapping and clipping.

Tags

[A] [B] [X] [Y] [START] [BACK] [GUIDE] [RT] [LT] [LB] [RB] [LTHUMB] [RTHUMB]

[NL] - adds a new line in place of \\n

Usage

//in game.LoadContent()
TextElement text = new TextElement();
text.LoadContent(Content, GraphicsDevice);
 
//in game.Draw()
text.Draw(spriteBatch);

Code

/// <summary>
    /// Represents formatted text with inline button glyphs, clipping and new lines.
    /// </summary>
    public class TextElement
    {
        private SpriteFont font, buttonFont;
        private Dictionary<string, string> buttonGlyphs = new Dictionary<string, string>();
        private float lineHeight = 0;
 
        public TextElement()
        {
            ButtonScale = 0.6f;
            Position = Vector2.Zero;
            Text = "Here is some example text. Press [A] or [START] to continue.";
            Color = Color.Black;
            Size = Vector2.Zero;
            ButtonScale = 0.6f;
            ClipText = false;
        }
 
        #region Properties
        private string text;
        public string Text
        {
            get { return text; }
            set
            {
                text = value;
                if (size != Vector2.Zero)
                    WrapText();
            }
        }
        public bool ClipText
        {
            get;
            set;
        }
        public Vector2 Position
        {
            get;
            set;
        }
        public Color Color
        {
            get;
            set;
        }
        private Vector2 size;
        public Vector2 Size
        {
            get { return size; }
            set
            {
                size = value;
                if (size != Vector2.Zero)
                    WrapText();
            }
        }
        public float ButtonScale
        {
            get;
            set;
        }
        #endregion
 
        public void LoadContent(ContentManager content, GraphicsDevice device)
        {
            buttonGlyphs = new Dictionary<string, string>();
            buttonGlyphs.Add("[LTHUMB]", " ");
            buttonGlyphs.Add("[RTHUMB]", "\"");
            buttonGlyphs.Add("[DPAD]", "!");
            buttonGlyphs.Add("[BACK]", "#");
            buttonGlyphs.Add("[GUIDE]", "$");
            buttonGlyphs.Add("[START]", "%");
            buttonGlyphs.Add("[X]", "&");
            buttonGlyphs.Add("[Y]", "(");
            buttonGlyphs.Add("[A]", "'");
            buttonGlyphs.Add("[B]", ")");
            buttonGlyphs.Add("[LB]", "-");
            buttonGlyphs.Add("[RB]", "*");
            buttonGlyphs.Add("[RT]", "+");
            buttonGlyphs.Add("[LT]", ",");
 
            font = content.Load<SpriteFont>("Font");
            buttonFont = content.Load<SpriteFont>("xboxControllerSpriteFont");
            WrapText();
        }
 
        public void Draw(SpriteBatch sb)
        {
            lineHeight = font.LineSpacing;
            if (ClipText)
            {
                sb.GraphicsDevice.RenderState.ScissorTestEnable = true;
                sb.GraphicsDevice.ScissorRectangle = new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
            }
            DrawText(Text, sb, Position);
            if (ClipText)
            {
                sb.End();
                sb.Begin();
                sb.GraphicsDevice.RenderState.ScissorTestEnable = false;
            }
        }
 
        private void DrawText(string text, SpriteBatch sb, Vector2 position)
        {
            int tagStart = text.IndexOf('[');
            int tagEnd = text.IndexOf(']', tagStart != -1 ? tagStart : 0);
            if (tagStart > 0)
                position += DrawString(text.Substring(0, tagStart), sb, position);
            if (tagStart != -1 && tagEnd != -1)
                position += DrawButton(text.Substring(tagStart, tagEnd - tagStart + 1), sb, position);
            if (tagEnd != -1)
                DrawText(text.Substring(tagEnd+1), sb, position);
            else
                DrawString(text, sb, position);
        }
 
        private Vector2 DrawString(string text, SpriteBatch sb, Vector2 position)
        {
            sb.DrawString(font, text, position, Color);
            return new Vector2(font.MeasureString(text).X, 0);
        }
 
        private Vector2 DrawButton(string tag, SpriteBatch sb, Vector2 position)
        {
            //Convert tag into button font text
            if (buttonGlyphs.ContainsKey(tag))
                tag = buttonGlyphs[tag];
            else if (tag == "[NL]")
            {
                Vector2 r = new Vector2(Position.X - position.X, lineHeight);
                lineHeight = font.LineSpacing;
                return r;
            }
            else
                return DrawString(tag, sb, position);
 
            Vector2 size = buttonFont.MeasureString(tag) * ButtonScale;
            if (lineHeight < size.Y * 0.5f) lineHeight = size.Y * 0.4f;
            //Draw string
            sb.DrawString(buttonFont, tag, position, Color.White, 0f, new Vector2(0, size.Y / 2), ButtonScale, SpriteEffects.None, 0);
            return new Vector2(size.X, 0);
        }
 
        private void WrapText()
        {
            if (font == null || Size == Vector2.Zero) return;
 
            string[] words = Text.Split(' ');
            StringBuilder sb = new StringBuilder();
 
            float lineWidth = 0f;
            float spaceWidth = font.MeasureString(" ").X;
 
            foreach (string word in words)
            {
                float size = MeasureWord(word);
                if (lineWidth + size > Size.X)
                {
                    sb.Append(word + "[NL]");
                    lineWidth = 0f;
                }
                else
                {
                    sb.Append(word);
                    lineWidth += size;
                }
            }
            text = sb.ToString();
        }
 
        private float MeasureWord(string text)
        {
            float length = 0f;
            int startIndex = text.IndexOf('[');
            int endIndex = text.IndexOf(']', startIndex != -1 ? startIndex : 0);
            if (startIndex > 0)
                length = font.MeasureString(text.Substring(0, startIndex)).X;
            if (startIndex != -1 && endIndex != -1)
                length += MeasureTag(text.Substring(startIndex, endIndex - startIndex + 1));
            if (endIndex != -1)
                length += MeasureWord(text.Substring(endIndex+1));
            else
                return font.MeasureString(text).X;
            if (length < 0) length = 0;
            return length;
        }
 
        private float MeasureTag(string tag)
        {
            if (buttonGlyphs.ContainsKey(tag))
                return buttonFont.MeasureString(buttonGlyphs[tag]).X;
            else if (tag == "[NL]")
                return float.NegativeInfinity;
            else
                return font.MeasureString(tag).X;
        }
    }