Created
June 13, 2020 23:55
-
-
Save Pilvinen/c3a858e2fabe87f3ead967dd15a25d98 to your computer and use it in GitHub Desktop.
Helpers for getting pixel sizes for Godot.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
/// <summary> | |
/// 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.</summary> | |
/// <param name="s">String to parse. Can be null or empty.</param> | |
/// <param name="delimiters">The delimiting strings. Can be an empty array.</param> | |
/// <returns></returns> | |
public static IList<string> SplitAndKeepDelimiters(string s, params string[] delimiters) { | |
var parts = new List<string>() { 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<string> 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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment