public static void CalculateLines(List <UITextEditLine> m_Lines, List <string> newWordsArray, TextStyle TextStyle, float lineWidth, float spaceWidth, Vector2 topLeftIconSpace, float lineHeight) { var currentLine = new StringBuilder(); var currentLineWidth = 0.0f; var currentLineNum = 0; for (var i = 0; i < newWordsArray.Count; i++) { var allowedWidth = (currentLineNum * lineHeight < topLeftIconSpace.Y)?lineWidth - topLeftIconSpace.X:lineWidth; var word = newWordsArray[i]; if (word == "\r\n") { /** Line break **/ m_Lines.Add(new UITextEditLine { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum, WhitespaceSuffix = 2 }); currentLineNum++; currentLine = new StringBuilder(); currentLineWidth = 0; } else { bool wordWritten = false; while (!wordWritten) //repeat until the full word is written (as part of it can be written each pass if it is too long) { var wordSize = TextStyle.MeasureString(word); if (wordSize.X > allowedWidth) { //SPECIAL CASE, word is bigger than line width and cannot fit on its own line if (currentLineWidth > 0) { //if there are words on this line, we'll start this one on the next to get the most space for it m_Lines.Add(new UITextEditLine { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum }); currentLineNum++; currentLine = new StringBuilder(); currentLineWidth = 0; } // binary search, makes this a bit faster? // we can safely say that no character is thinner than 4px, so set max substring to maxwidth/4 float width = allowedWidth + 1; int min = 1; int max = Math.Min(word.Length, (int)allowedWidth / 4); int mid = (min + max) / 2; while (max - min > 1) { width = TextStyle.MeasureString(word.Substring(0, mid)).X; if (width > allowedWidth) { max = mid; } else { min = mid; } mid = (max + min) / 2; } currentLine.Append(word.Substring(0, min)); currentLineWidth += width; word = word.Substring(min); m_Lines.Add(new UITextEditLine { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum, WhitespaceSuffix = 1 }); currentLineNum++; currentLine = new StringBuilder(); currentLineWidth = 0; } else if (currentLineWidth + wordSize.X < allowedWidth) { currentLine.Append(word); if (i != newWordsArray.Count - 1) { currentLine.Append(' '); currentLineWidth += spaceWidth; } currentLineWidth += wordSize.X; wordWritten = true; } else { /** New line **/ m_Lines.Add(new UITextEditLine { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum, WhitespaceSuffix = 1 }); currentLineNum++; currentLine = new StringBuilder(); currentLine.Append(word); currentLineWidth = wordSize.X; if (i != newWordsArray.Count - 1) { currentLine.Append(' '); currentLineWidth += spaceWidth; } wordWritten = true; } } } } m_Lines.Add(new UITextEditLine //add even if length is 0, so we can move the cursor down! { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum }); var currentIndex = 0; foreach (var line in m_Lines) { line.StartIndex = currentIndex; currentIndex += (line.Text.Length - 1) + line.WhitespaceSuffix; } }
/// <summary> /// This utility will draw a line of text onto the UIElement. /// </summary> /// <param name="batch">The SpriteBatch to draw the text onto</param> /// <param name="text">The content of the text</param> /// <param name="to">The position of the text. Relative to this UIElement.</param> /// <param name="style">The text style</param> /// <param name="bounds">Rectangle relative to this UIElement which the text should be positioned within</param> /// <param name="align">Alignment of the text within the bounds box.</param> /// <param name="margin">Margin offset from the bounding box.</param> /// <param name="state">State of the text, e.g. hover, down, normal</param> public void DrawLocalString(SpriteBatch batch, string text, Vector2 to, TextStyle style, Rectangle bounds, TextAlignment align, Rectangle margin, UIElementState state) { //TODO: We should find some way to cache this data /** * Work out the scale of the vector font. * * We need to scale it based on the UIElement's scale factory, * but we also need to scale it based on the text styles scale factor. * * Aka if the vector font is 12px and we asked for 24px it would be scale of 2.0 */ var scale = _Scale; if (style.Scale != 1.0f) { scale = new Vector2(scale.X * style.Scale, scale.Y * style.Scale); } /** Work out how big the text will be so we can align it **/ var size = (align == 0) ? Vector2.Zero : style.MeasureString(text); /** Apply margins **/ if (margin != Rectangle.Empty) { bounds.X += margin.X; bounds.Y += margin.Y; bounds.Width -= margin.Right; bounds.Height -= margin.Bottom; } /** Work out X and Y based on alignment & bounding box **/ var pos = to; pos.X += bounds.X; pos.Y += bounds.Y; if ((align & TextAlignment.Right) == TextAlignment.Right) { pos.X += (bounds.Width - size.X); } else if ((align & TextAlignment.Center) == TextAlignment.Center) { pos.X += (bounds.Width - size.X) / 2; } if ((align & TextAlignment.Middle) == TextAlignment.Middle) { pos.Y += (bounds.Height - size.Y) / 2; } else if ((align & TextAlignment.Bottom) == TextAlignment.Bottom) { pos.Y += (bounds.Height - size.Y); } //pos.Y += style.BaselineOffset; /** Draw the string **/ pos = FlooredLocalPoint(pos); if (style.VFont != null) { batch.End(); Matrix?mat = null; var ui = (batch as UISpriteBatch); if (ui != null && ui.BatchMatrixStack.Count > 0) { mat = ui.BatchMatrixStack.Peek(); } if (style.Shadow) { style.VFont.Draw(batch.GraphicsDevice, text, pos + new Vector2(FSOEnvironment.DPIScaleFactor), Color.Black, scale, mat); } style.VFont.Draw(batch.GraphicsDevice, text, pos, style.GetColor(state) * Opacity, scale, mat); if (mat != null) { batch.Begin(transformMatrix: mat, rasterizerState: RasterizerState.CullNone); } else { batch.Begin(rasterizerState: RasterizerState.CullNone); } } else { if (style.Shadow) { batch.DrawString(style.SpriteFont, text, pos + new Vector2(FSOEnvironment.DPIScaleFactor), Color.Black, 0, Vector2.Zero, scale, SpriteEffects.None, 0); } batch.DrawString(style.SpriteFont, text, pos, style.GetColor(state) * Opacity, 0, Vector2.Zero, scale, SpriteEffects.None, 0); } }
public static void CalculateLines(List<UITextEditLine> m_Lines, List<string> newWordsArray, TextStyle TextStyle, float lineWidth, float spaceWidth, Vector2 topLeftIconSpace, float lineHeight) { var currentLine = new StringBuilder(); var currentLineWidth = 0.0f; var currentLineNum = 0; for (var i = 0; i < newWordsArray.Count; i++) { var allowedWidth = (currentLineNum*lineHeight<topLeftIconSpace.Y)?lineWidth-topLeftIconSpace.X:lineWidth; var word = newWordsArray[i]; if (word == "\r\n") { /** Line break **/ m_Lines.Add(new UITextEditLine { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum, WhitespaceSuffix = 2 }); currentLineNum++; currentLine = new StringBuilder(); currentLineWidth = 0; } else { bool wordWritten = false; while (!wordWritten) //repeat until the full word is written (as part of it can be written each pass if it is too long) { var wordSize = TextStyle.MeasureString(word); if (wordSize.X > allowedWidth) { //SPECIAL CASE, word is bigger than line width and cannot fit on its own line if (currentLineWidth > 0) { //if there are words on this line, we'll start this one on the next to get the most space for it m_Lines.Add(new UITextEditLine { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum }); currentLineNum++; currentLine = new StringBuilder(); currentLineWidth = 0; } float width = allowedWidth + 1; int j = word.Length; while (width > allowedWidth) { width = TextStyle.MeasureString(word.Substring(0, --j)).X; } currentLine.Append(word.Substring(0, j)); currentLineWidth += width; word = word.Substring(j); m_Lines.Add(new UITextEditLine { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum, WhitespaceSuffix = 1 }); currentLineNum++; currentLine = new StringBuilder(); currentLineWidth = 0; } else if (currentLineWidth + wordSize.X < allowedWidth) { currentLine.Append(word); if (i != newWordsArray.Count - 1) { currentLine.Append(' '); currentLineWidth += spaceWidth; } currentLineWidth += wordSize.X; wordWritten = true; } else { /** New line **/ m_Lines.Add(new UITextEditLine { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum, WhitespaceSuffix = 1 }); currentLineNum++; currentLine = new StringBuilder(); currentLine.Append(word); currentLineWidth = wordSize.X; if (i != newWordsArray.Count - 1) { currentLine.Append(' '); currentLineWidth += spaceWidth; } wordWritten = true; } } } } m_Lines.Add(new UITextEditLine //add even if length is 0, so we can move the cursor down! { Text = currentLine.ToString(), LineWidth = currentLineWidth, LineNumber = currentLineNum }); var currentIndex = 0; foreach (var line in m_Lines) { line.StartIndex = currentIndex; currentIndex += (line.Text.Length - 1) + line.WhitespaceSuffix; } }