Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Helpers for getting pixel sizes for Godot.
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