public MultilineString(string text, Rectangle rect, TextAlignment alignment, BitmapFontGroup font, List <MultilineFragment> cachedLines, Color color, bool degrading) { this.CachedLines = cachedLines; this.text = text; rectangle = rect; textAlignment = alignment; this.font = font; this.color = color; this.degrading = degrading; }
/// <summary> /// Draws a multiline text using the Primitives spritebatch. /// </summary> /// <param name="text">Text to be drawn.</param> /// <param name="rectangle">The rectangle bounding the text.</param> /// <param name="color">Text color.</param> /// <param name="font">Text font (Verdana 14, if null).</param> /// <param name="degrading"></param> /// <param name="alignment">Text alignment.</param> public static void DrawString(string text, Rectangle rectangle, Color?color = null, BitmapFontGroup font = null, TextAlignment alignment = TextAlignment.TopLeft, bool degrading = false) { // TODO complete degrading Color baseColor = color ?? Color.Black; BitmapFontGroup baseFont = font ?? BitmapFontGroup.DefaultFont; MultilineString ms = new MultilineString(text, rectangle, alignment, baseFont, null, baseColor, degrading); MultilineString msCache = MultilineStringCache.GetValueOrDefault(ms, null); if (msCache != null) { foreach (MultilineFragment line in msCache.CachedLines) { if (line.Icon != null) { SpriteBatch.Draw(line.Icon, new Rectangle(rectangle.X + (int)line.PositionOffset.X, rectangle.Y + (int)line.PositionOffset.Y, line.IconWidth, line.IconWidth), Color.White); } else { SpriteBatch.DrawString(line.Font, line.Text, new Vector2(rectangle.X + (int)line.PositionOffset.X, rectangle.Y + (int)line.PositionOffset.Y), line.Color); } } return; } Rectangle rrr; SetupNewMultilineString(baseFont, text, rectangle, baseColor, alignment, out rrr, out List <MultilineFragment> cachedLines, degrading); var multilineString = new MultilineString(text, rectangle, alignment, baseFont, cachedLines, baseColor, degrading); MultilineStringCache.Add(multilineString, multilineString); DrawString(text, rectangle, baseColor, baseFont, alignment); }
public BitmapFontGroup(BitmapFont regular, BitmapFont italics, BitmapFont bold, BitmapFont boldItalics, BitmapFontGroup degradesTo = null) { DegradesTo = degradesTo; Regular = regular; Italics = italics; Bold = bold; BoldItalics = boldItalics; }
/// <summary> /// If the text were written to the specified rectangle, how much width and height would it actually use? /// This method ignores the rectangle's X and Y properties. /// </summary> /// <param name="text">Text to draw.</param> /// <param name="rectangle">Rectangle bounding the text.</param> /// <param name="font">Font to use.</param> public static Rectangle GetMultiLineTextBounds(string text, Rectangle rectangle, BitmapFontGroup font = null) { Rectangle bounds; List <MultilineFragment> _; SetupNewMultilineString(font ?? BitmapFontGroup.DefaultFont, text, rectangle, Color.Black, TextAlignment.TopLeft, out bounds, out _, false); return(bounds); }
/// <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(BitmapFontGroup fnt, string text, Rectangle r, Color col, TextAlignment align, out Rectangle textBounds, out List <MultilineFragment> cachedLines, bool degrading) { textBounds = r; if (text == string.Empty) { cachedLines = new List <MultilineFragment>(); return; } Data d = new Data(); d.FontGroup = 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.LineHeight; d.Color = col; d.End = false; d.Lines = new List <List <MultilineFragment> >(); d.ThisLine = new List <MultilineFragment>(); if (d.Height >= d.LineHeight) { foreach (char t in text) { if (t == '{') { // Flush and write. 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 { FlushAndWrite(d); GoToNextLine(d); d.ThisLine.Add(new MultilineFragment(icon, new Vector2(d.CurrentX, d.CurrentY), iconwidth)); d.CurrentX += iconwidth + 3; } break; } else if (tag[0] == '/') { d.Color = col; } else { d.Color = ColorFromString(tag); } 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()).Width <= d.Width - d.CurrentX) { // It will fit. d.ReadyFragment.Append(' '); d.Builder.Clear(); } else { // It would not fit. d.ReadyFragment = new StringBuilder(without); string newone = d.Builder.ToString(); d.Builder = new StringBuilder(); FlushAndWrite(d, true); if (!d.End) { d.Builder.Append(newone); FlushAndWrite(d); d.Builder.Clear(); d.Builder.Append(' '); } } // Write if overflowing. } else if (t == '\n') { // Flush and write. FlushAndWrite(d); // Skip to new line automatically. GoToNextLine(d); } else { d.Builder.Append(t); } if (d.End) { break; } } // Flush and write. FlushAndWrite(d); if (d.ThisLine.Count > 0) { FinishLine(d); d.TotalNumberOfLines += 1; } } if (d.End && degrading && fnt.DegradesTo != null) { SetupNewMultilineString(fnt.DegradesTo, text, r, col, align, out textBounds, out cachedLines, degrading); return; } // 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; } cachedLines = new List <MultilineFragment>(); 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); } } }