/// <summary> /// Draws a multi-string. /// WARNING! This grows more CPU-intensive as the number of words grow (only if word wrap enabled). It is recommended to use the DrawMultiLineText method instead - it uses caching. /// </summary> /// <param name="fnt">A reference to a SpriteFont object.</param> /// <param name="text">The text to be drawn. <remarks>If the text contains \n it /// will be treated as a new line marker and the text will drawn acordingy.</remarks></param> /// <param name="r">The screen rectangle that the rext should be drawn inside of.</param> /// <param name="col">The color of the text that will be drawn.</param> /// <param name="align">Specified the alignment within the specified screen rectangle.</param> /// <param name="textBounds">Returns a rectangle representing the size of the bouds of /// the text that was drawn.</param> /// <param name="cachedLines">This parameter is internal. Do not use it, merely throw away the variable.</param> private static void SetupNewMultilineString(FontFamily fnt, string text, Rectangle r, Color col, TextAlignment align, out Rectangle textBounds, out List <MultilineFragment> cachedLines) { textBounds = r; cachedLines = new List <MultilineFragment>(); if (text == string.Empty) { return; } Data d = new Data(); d.FontGroup = Fonts[fnt]; d.ReadyFragment = new StringBuilder(); d.Builder = new StringBuilder(); d.CurrentFont = d.FontGroup.Regular; d.CurrentX = 0; d.CurrentY = 0; d.TotalNumberOfLines = 0; d.Width = r.Width; d.Height = r.Height; d.IsBold = false; d.IsItalics = false; d.LineHeight = d.FontGroup.Regular.LineSpacing; d.Color = col; d.End = false; d.Lines = new List <List <MultilineFragment> >(); d.ThisLine = new List <MultilineFragment>(); foreach (char t in text) { if (t == '{') { // Flush and write. Primitives.FlushAndWrite(d); // Ready to change mode. } else if (t == '}') { // Change mode. switch (d.Builder.ToString()) { case "b": d.IsBold = !d.IsBold; d.UpdateFont(); break; case "i": d.IsItalics = !d.IsItalics; d.UpdateFont(); break; case "/b": d.IsBold = !d.IsBold; d.UpdateFont(); break; case "/i": d.IsItalics = !d.IsItalics; d.UpdateFont(); break; default: string tag = d.Builder.ToString(); if (tag.StartsWith("icon:")) { Texture2D icon = Library.Icons[tag.Substring(5)]; d.Builder.Clear(); // Now add icon. int iconwidth = d.LineHeight; int remainingSpace = (int)(d.Width - d.CurrentX); if (remainingSpace > iconwidth + 3) { d.ThisLine.Add(new MultilineFragment(icon, new Vector2(d.CurrentX, d.CurrentY), iconwidth)); d.CurrentX += iconwidth + 3; } else { Primitives.FlushAndWrite(d); Primitives.GoToNextLine(d); d.ThisLine.Add(new MultilineFragment(icon, new Vector2(d.CurrentX, d.CurrentY), iconwidth)); d.CurrentX += iconwidth + 3; } break; } d.Color = Primitives.ColorFromString(tag); if (tag[0] == '/') { d.Color = col; } break; } d.Builder.Clear(); } else if (t == ' ') { // Flush. // Add builder to ready. string without = d.ReadyFragment.ToString(); d.ReadyFragment.Append(d.Builder); if (d.CurrentFont.MeasureString(d.ReadyFragment.ToString()).X <= d.Width - d.CurrentX) { // It will fit. d.Builder.Clear(); d.Builder.Append(' '); } else { // It would not fit. d.ReadyFragment = new StringBuilder(without); string newone = d.Builder.ToString(); d.Builder = new StringBuilder(); Primitives.FlushAndWrite(d); Primitives.GoToNextLine(d); d.Builder.Append(newone); Primitives.FlushAndWrite(d); d.Builder.Clear(); d.Builder.Append(' '); } // Write if overflowing. } else if (t == '\n') { // Flush and write. Primitives.FlushAndWrite(d); // Skip to new line automatically. Primitives.GoToNextLine(d); } else { d.Builder.Append(t); } if (d.End) { break; } } // Flush and write. FlushAndWrite(d); FinishLine(d); d.TotalNumberOfLines += 1; // Modify for non-top-left. // Output. textBounds = new Rectangle(r.X, r.Y, (int)d.Maximumwidthencountered, d.TotalNumberOfLines * d.LineHeight); int yoffset = 0; switch (align) { case TextAlignment.TopLeft: case TextAlignment.Top: case TextAlignment.TopRight: break; case TextAlignment.Bottom: case TextAlignment.BottomLeft: case TextAlignment.BottomRight: yoffset = r.Height - d.TotalNumberOfLines * d.LineHeight; break; case TextAlignment.Middle: case TextAlignment.Left: case TextAlignment.Right: yoffset = (r.Height - (d.TotalNumberOfLines * d.LineHeight)) / 2; break; } foreach (var line in d.Lines) { foreach (var fragment in line) { if (fragment.Text == "") { continue; } float xoffset = 0; float lineWidth = line.Sum(frg => frg.Width); switch (align) { case TextAlignment.Right: case TextAlignment.TopRight: case TextAlignment.BottomRight: xoffset = r.Width - lineWidth; break; case TextAlignment.Middle: case TextAlignment.Top: case TextAlignment.Bottom: xoffset = (r.Width - lineWidth) / 2; break; } fragment.PositionOffset += new Vector2(xoffset, yoffset); fragment.PositionOffset = new Vector2((int)fragment.PositionOffset.X, (int)fragment.PositionOffset.Y); cachedLines.Add(fragment); } } }