using System; using System.Collections.Generic; using Godot; namespace Haikuchatclient { public static class PixelTools { // Find out message height in pixels. // ********************************** // Can be used to set surrounding elements of Labels etc. to a proper height. public static int GetPixelHeightForText(string text, DynamicFont dynamicFont, int surroundingElementWidth) { // Get line height in pixels, ie. font height. It's always standard AFAIK. var fontHeight = dynamicFont.GetHeight(); // Split the text into an array using spaces as delimiters - and include the spaces. var splitText = SplitAndKeepDelimiters(text, " "); // Calculate how many lines of text there will be. var lineCount = CalculateLines(splitText, dynamicFont, surroundingElementWidth); // Calculate the pixel height needed to fit in the text. var pixelHeight = lineCount * fontHeight;// + lineSpacing; // Account for the outline surrounding the font. pixelHeight += (dynamicFont.OutlineSize * lineCount); // Return the result. return (int) pixelHeight; } /// /// Splits the given string into a list of substrings, while outputting the splitting /// delimiters (each in its own string) as well. It's just like String.Split() except /// the delimiters are preserved. No empty strings are output. /// String to parse. Can be null or empty. /// The delimiting strings. Can be an empty array. /// public static IList SplitAndKeepDelimiters(string s, params string[] delimiters) { var parts = new List() { s }; if (!string.IsNullOrEmpty(s)) { foreach (string delimiter in delimiters) { // Delimiter counter. for (int i = 0; i < parts.Count; i++) { // Part counter. int index = parts[i].IndexOf(delimiter, StringComparison.Ordinal); if (index > -1 && parts[i].Length > index + 1) { string leftPart = parts[i].Substring(0, index + delimiter.Length); string rightPart = parts[i].Substring(index + delimiter.Length); parts[i] = leftPart; parts.Insert(i + 1, rightPart); } } } } return parts; } private static int CalculateLines(IList splitText, DynamicFont dynamicFont, int surroundingElementWidth) { // Keeps track how much pixels we have left before we need to wrap to a next line. var widthLeftUntilNextLine = surroundingElementWidth; // Get last index position so we can refer to it. var lastIndex = splitText.Count - 1; // We need to keep track of the current index so we know when we hit the last one - so we can add last line. var indexCounter = 0; // We start from line 1, because there is always at least one line. var totalLines = 1; // Iterate through the split message to find out the amount of lines the message will split to. foreach (var word in splitText) { // Let's find out how many pixels there are in this word. var pixelsInWord = GetWordPixelWidth(word, dynamicFont); // If the amount of pixels is more than one line can handle we go to the next line. if (pixelsInWord > widthLeftUntilNextLine) { totalLines += 1; // TODO: Code will bug out if the word is too long. One way to handle it is to do manual line breaks. // Subtract this word's width from the next line because this word didn't fit on last line. widthLeftUntilNextLine = surroundingElementWidth - pixelsInWord; // We can handle some more words. No line break yet. } else if (pixelsInWord == widthLeftUntilNextLine) { // Prevent border case where we don't need the extra line. if (indexCounter != lastIndex) { totalLines += 1; } widthLeftUntilNextLine = surroundingElementWidth; } else { // Reduce the word's length from the pixels left for this line. widthLeftUntilNextLine -= pixelsInWord; } // Increase the index for the next round. indexCounter += 1; } return totalLines; } // Return the total width of a string in pixels. // TODO: Could this be turned into an extension method? public static int GetWordPixelWidth(string word, DynamicFont dynamicFont) { return (int) dynamicFont.GetStringSize(word).x; } } }