From 91c53c41c218e6638e909e9613c4201c23ea72f5 Mon Sep 17 00:00:00 2001 From: Relintai Date: Wed, 30 Nov 2022 14:57:35 +0100 Subject: [PATCH] Ported: Fix String::word_wrap() for long words - timothyqiu https://github.com/godotengine/godot/commit/51fd1c27aa555a1be57e903305642d5d0bd82001 --- core/string/ustring.cpp | 71 ++++++++++++++++++++++++---------- main/tests/test_string.cpp.old | 36 +++++++++++++++++ 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 309ac77cd..da0e42879 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -4414,32 +4414,63 @@ String String::json_escape() const { return escaped; } -//kind of poor should be rewritten properly String String::word_wrap(int p_chars_per_line) const { - int from = 0; - int last_space = 0; String ret; + + int line_start = 0; + int line_end = 0; // End of last word on current line. + int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word. + int word_length = 0; + for (int i = 0; i < length(); i++) { - if (i - from >= p_chars_per_line) { - if (last_space == -1) { - ret += substr(from, i - from + 1) + "\n"; - } else { - ret += substr(from, last_space - from) + "\n"; - i = last_space; //rewind - } - from = i + 1; - last_space = -1; - } else if (operator[](i) == ' ' || operator[](i) == '\t') { - last_space = i; - } else if (operator[](i) == '\n') { - ret += substr(from, i - from) + "\n"; - from = i + 1; - last_space = -1; + const CharType c = operator[](i); + + switch (c) { + case '\n': { + // Force newline. + ret += substr(line_start, i - line_start + 1); + line_start = i + 1; + line_end = line_start; + word_start = line_start; + word_length = 0; + } break; + + case ' ': + case '\t': { + // A whitespace ends current word. + if (word_length > 0) { + line_end = i - 1; + word_start = -1; + word_length = 0; + } + } break; + + default: { + if (word_start == -1) { + word_start = i; + } + word_length += 1; + + if (word_length > p_chars_per_line) { + // Word too long: wrap before current character. + ret += substr(line_start, i - line_start) + "\n"; + line_start = i; + line_end = i; + word_start = i; + word_length = 1; + } else if (i - line_start + 1 > p_chars_per_line) { + // Line too long: wrap after the last word. + ret += substr(line_start, line_end - line_start + 1) + "\n"; + line_start = word_start; + line_end = line_start; + } + } break; } } - if (from < length()) { - ret += substr(from, length()); + const int remaining = length() - line_start; + if (remaining) { + ret += substr(line_start, remaining); } return ret; diff --git a/main/tests/test_string.cpp.old b/main/tests/test_string.cpp.old index 1eb06d745..f4c8fd114 100644 --- a/main/tests/test_string.cpp.old +++ b/main/tests/test_string.cpp.old @@ -1219,6 +1219,41 @@ bool test_36() { return true; } +bool test_37() { +#define CHECK_EQ(X, Y) \ + if ((X) != (Y)) { \ + OS::get_singleton()->print("\tFAIL: %s != %s\n", #X, #Y); \ + return false; \ + } else { \ + OS::get_singleton()->print("\tPASS\n"); \ + } + OS::get_singleton()->print("\n\nTest 37: Word wrap\n"); + + // Long words. + CHECK_EQ(String("12345678").word_wrap(8), "12345678"); + CHECK_EQ(String("1234567812345678").word_wrap(8), "12345678\n12345678"); + CHECK_EQ(String("123456781234567812345678").word_wrap(8), "12345678\n12345678\n12345678"); + + // Long line. + CHECK_EQ(String("123 567 123456 123").word_wrap(8), "123 567\n123456\n123"); + + // Force newline at line length should not create another newline. + CHECK_EQ(String("12345678 123").word_wrap(8), "12345678\n123"); + CHECK_EQ(String("12345678\n123").word_wrap(8), "12345678\n123"); + + // Wrapping removes spaces. + CHECK_EQ(String("1234567 123").word_wrap(8), "1234567\n123"); + CHECK_EQ(String("12345678 123").word_wrap(8), "12345678\n123"); + + // Wrapping does not remove leading space. + CHECK_EQ(String(" 123456 123 12").word_wrap(8), " 123456\n123 12"); + CHECK_EQ(String(" 123456\n 456 12").word_wrap(8), " 123456\n 456\n12"); + CHECK_EQ(String(" 123456\n 4 12345678").word_wrap(8), " 123456\n 4\n12345678"); + CHECK_EQ(String(" 123456\n 4 12345678123").word_wrap(8), " 123456\n 4\n12345678\n123"); + + return true; +} + typedef bool (*TestFunc)(); TestFunc test_funcs[] = { @@ -1259,6 +1294,7 @@ TestFunc test_funcs[] = { test_34, test_35, test_36, + test_37, nullptr };