Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 78 additions & 41 deletions src/main/java/betterquesting/api/utils/RenderUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ public static void drawHighlightedSplitString(FontRenderer renderer, String stri
List<String> list = splitString(string, width, renderer);
List<String> noFormat = splitStringWithoutFormat(string, width, renderer); // Needed for accurate highlight index positions

if (list.size() != noFormat.size()) {
if (list.size() != noFormat.size() || (list.isEmpty() && noFormat.isEmpty())) {
//BetterQuesting.logger.error("Line count mismatch (" + list.size() + " != " + noFormat.size() + ") while drawing formatted text!");
return;
}
Expand Down Expand Up @@ -477,20 +477,26 @@ public static List<String> splitStringWithoutFormat(String str, int wrapWidth, F
String temp = str;

while (true) {
int i = sizeStringToWidth(lastFormat + temp, wrapWidth, font); // Cut to size WITH formatting
i -= lastFormat.length(); // Remove formatting characters from count
int stringWidth = sizeStringToWidth(lastFormat + temp, wrapWidth, font); // Cut to size WITH formatting
stringWidth -= lastFormat.length(); // Remove formatting characters from count

if (temp.length() <= i) {
if (temp.length() <= stringWidth) {
list.add(temp);
break;
}
// Break in case of very squished cases, preventing an infinite loop
// StringWidth is the index just before the wrap width would have been exceeded (or a newline), so we want to
// include up to it in the length check
else if (font.getStringWidth(temp.substring(0, stringWidth + 1)) > wrapWidth) {
break;
} else {
String s = temp.substring(0, i);
char c0 = temp.charAt(i);
boolean flag = c0 == ' ' || c0 == '\n';
lastFormat = FontRenderer.getFormatFromString(lastFormat + s);
temp = temp.substring(i + (flag ? 1 : 0));
String partialString = temp.substring(0, stringWidth);
char finalChar = temp.charAt(stringWidth);
boolean flag = finalChar == ' ' || finalChar == '\n';
lastFormat = FontRenderer.getFormatFromString(lastFormat + partialString);
temp = temp.substring(stringWidth + (flag ? 1 : 0));
// NOTE: The index actually stops just before the space/nl so we don't need to remove it from THIS line. This is why the previous line moves forward by one for the NEXT line
list.add(s + (flag ? "\n" : "")); // Although we need to remove the spaces between each line we have to replace them with invisible new line characters to preserve the index count
list.add(partialString + (flag ? "\n" : "")); // Although we need to remove the spaces between each line we have to replace them with invisible new line characters to preserve the index count

if (temp.length() <= 0 && !flag) {
break;
Expand All @@ -501,25 +507,45 @@ public static List<String> splitStringWithoutFormat(String str, int wrapWidth, F
return list;
}

/**
* Splits a String into multiple Strings depending on a maximum width
* @param str The original String
* @param wrapWidth The maximum width
* @param font The FontRenderer
* @return A List of Strings split based on the maximum width
*/
public static List<String> splitString(String str, int wrapWidth, FontRenderer font) {
List<String> list = new ArrayList<>();

String temp = str;

while (true) {
int i = sizeStringToWidth(temp, wrapWidth, font); // Cut to size WITH formatting

if (temp.length() <= i) {
// Splits the String to the maximum width, taking into account formatting
int stringWidth = sizeStringToWidth(temp, wrapWidth, font); // Cut to size WITH formatting

// If the String does not need to be cut, return the original String
if (temp.length() <= stringWidth) {
list.add(temp);
break;
} else {
String s = temp.substring(0, i);
char c0 = temp.charAt(i);
boolean flag = c0 == ' ' || c0 == '\n';
temp = FontRenderer.getFormatFromString(s) + temp.substring(i + (flag ? 1 : 0));
list.add(s);

if (temp.length() <= 0 && !flag) {
}
// Break in case of very squished cases, preventing an infinite loop
// StringWidth is the index just before the wrap width would have been exceeded (or a newline), so we want to
// include up to it in the length check
else if (font.getStringWidth(temp.substring(0, stringWidth + 1)) > wrapWidth) {
break;
}
else {
String partialString = temp.substring(0, stringWidth);
char finalChar = temp.charAt(stringWidth);
// Detects if the final Char is a space or a line break
boolean flag = finalChar == ' ' || finalChar == '\n';
// Sets the temp String to any formatting characters, designated by \u00a7 or char 167, and adds on the space/line break if required
// This can be an empty string, if there are no formattings applied and no space or line break
temp = FontRenderer.getFormatFromString(partialString) + temp.substring(stringWidth + (flag ? 1 : 0));
list.add(partialString);

if (temp.length() <= 0) {
break;
}
}
Expand Down Expand Up @@ -576,56 +602,67 @@ public static int getCursorPos(String text, int x, int y, int width, FontRendere
}

private static int sizeStringToWidth(String str, int wrapWidth, FontRenderer font) {
int i = str.length();
int j = 0;
int k = 0;
int l = -1;
int stringLength = str.length();
int charWidth = 0;
int index = 0;
int breakIndex = -1;

for (boolean flag = false; k < i; ++k) {
char c0 = str.charAt(k);
for (boolean flag = false; index < stringLength; ++index) {
char selectedCharacter = str.charAt(index);

switch (c0) {
switch (selectedCharacter) {
case '\n':
--k;
--index;
break;
case ' ':
l = k;
breakIndex = index;
default:
j += font.getCharWidth(c0);
charWidth += font.getCharWidth(selectedCharacter);

// If we are dealing with a specified formatting character, increment again
if (flag) {
++j;
++charWidth;
}

break;
// Minecraft formatting symbol
case '\u00a7':

if (k < i - 1) {
++k;
char c1 = str.charAt(k);
// Check if there can be a formatting character after the formatting symbol
if (index < stringLength - 1) {
++index;
char formattingCharacter = str.charAt(index);

if (c1 != 'l' && c1 != 'L') {
if (c1 == 'r' || c1 == 'R' || isFormatColor(c1)) {
// Ignore Bolded characters (why?)
if (formattingCharacter != 'l' && formattingCharacter != 'L') {
// If the formatting character is reset or a color code, set the flag to false
if (formattingCharacter == 'r' || formattingCharacter == 'R' || isFormatColor(formattingCharacter)) {
flag = false;
}
} else {
// Strikethrough, italic, underline, obfuscated
flag = true;
}
}
}

if (c0 == '\n') {
++k;
l = k;
// If the selected character was a new line, undo the index decrement and set the break index
// Doing this outside the switch ensures breaking the string on every new line
if (selectedCharacter == '\n') {
++index;
breakIndex = index;
break;
}

if (j > wrapWidth) {
// If our total string width exceeds the maximum allowed size, don't add anything else
if (charWidth > wrapWidth) {
break;
}
}

return k != i && l != -1 && l < k ? l : k;
// If we have not made it to the end of the string, and there are spaces or newlines that are not on the end of the string,
// return the index of the last index of a space or newline. Else return the index where we have exceeded the allowed width.
return index != stringLength && breakIndex != -1 && breakIndex < index ? breakIndex : index;
}

private static boolean isFormatColor(char colorChar) {
Expand Down