Skip to content

Commit

Permalink
Refactor wordwrap
Browse files Browse the repository at this point in the history
* Fixes hang when string.size() > 50 characters and has no spaces.
* Fixes width used when word wrap is called. Current one cuts too early.
* Unit tests for different strings
* Graphical adjustments to ensure long lines get rendered properly. The
  standard RPG_RT fonts use 50 characters. Previously we would render
  half of the 51st character as the drawing bitmap was not bounded
  correctly.
  • Loading branch information
fmatthew5876 committed Dec 16, 2018
1 parent dd41bee commit e307071
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 26 deletions.
50 changes: 26 additions & 24 deletions src/game_message.cpp
Expand Up @@ -174,41 +174,43 @@ int Game_Message::GetRealPosition() {
}
}

int Game_Message::WordWrap(const std::string& line, int limit, const std::function<void(const std::string &line)> callback) {
size_t start = 0;
size_t lastfound = 0;
int Game_Message::WordWrap(const std::string& line, const int limit, const std::function<void(const std::string &line)> callback) {
int start = 0;
int line_count = 0;
bool end_of_string;
FontRef font = Font::Default();
Rect size;

do {
line_count++;
size_t found = line.find(" ", start);
std::string wrapped = line.substr(start, found - start);
end_of_string = false;
int next = start;
do {
lastfound = found;
found = line.find(" ", lastfound + 1);
auto found = line.find(" ", next);
if (found == std::string::npos) {
found = line.size();
}
wrapped = line.substr(start, found - start);
size = font->GetSize(wrapped);
} while (found < line.size() - 1 && size.width < limit);
if (found >= line.size() - 1) {
// It's end of the string, not a word-break
if (size.width < limit) {
// And the last word of the string fits on the line
// (otherwise do another word-break)
lastfound = found;
end_of_string = true;

auto wrapped = line.substr(start, found - start);
auto width = font->GetSize(wrapped).width;
if (width > limit) {
if (next == start) {
next = found + 1;
}
break;
}

next = found + 1;
} while(next < line.size());

if (start == (next - 1)) {
start = next;
continue;
}
wrapped = line.substr(start, lastfound - start);

auto wrapped = line.substr(start, (next - 1) - start);

callback(wrapped);
start = lastfound + 1;
} while (start < line.size() && !end_of_string);
line_count++;

start = next;
} while (start < line.size());

return line_count;
}
4 changes: 2 additions & 2 deletions src/window_battlemessage.cpp
Expand Up @@ -29,7 +29,7 @@ Window_BattleMessage::Window_BattleMessage(int ix, int iy, int iwidth, int iheig
needs_refresh(true),
hidden_lines(0) {

SetContents(Bitmap::Create(width - 16, height - 16));
SetContents(Bitmap::Create(width - 20, height - 16));

visible = false;
// Above other windows but below the messagebox
Expand All @@ -45,7 +45,7 @@ void Window_BattleMessage::Push(const std::string& message) {
std::vector<std::string>& wrapped_lines = lines;
int line_count = Game_Message::WordWrap(
line,
GetWidth() - 24,
GetWidth() - 20,
[&wrapped_lines](const std::string& line) {
wrapped_lines.push_back(line);
}
Expand Down
95 changes: 95 additions & 0 deletions tests/wordwrap.cpp
@@ -0,0 +1,95 @@
#include <gtest/gtest.h>
#include "game_message.h"
#include "options.h"

constexpr int limit_2k = SCREEN_TARGET_WIDTH - 20;

static std::vector<std::string> WordWrap(const std::string& line, int limit = limit_2k) {
std::vector<std::string> lines;
Game_Message::WordWrap(line, limit, [&](const std::string& l) { lines.push_back(l); });
return lines;
}


TEST(WordWrapTest, Empty) {
auto lines = WordWrap("");
ASSERT_EQ(lines.size(), 0);

lines = WordWrap("", 0);
ASSERT_EQ(lines.size(), 0);

lines = WordWrap("a", 0);
ASSERT_EQ(lines.size(), 1);
ASSERT_EQ(lines[0], "a");
}

TEST(WordWrapTest, 1char) {
auto lines = WordWrap("a");
ASSERT_EQ(lines.size(), 1);
ASSERT_EQ(lines[0], "a");
}

TEST(WordWrapTest, normal) {
std::string line = "Skeleton Attacks!";
auto lines = WordWrap(line);
ASSERT_EQ(lines.size(), 1);
ASSERT_EQ(lines[0], line);

line = "Alex takes 300 damage!";
lines = WordWrap(line);
ASSERT_EQ(lines.size(), 1);
ASSERT_EQ(lines[0], line);
}

TEST(WordWrapTest, toolong) {
std::string base;
for (int i = 0; i < 6; ++i) {
base += "Skeleton";
}
auto lines = WordWrap(base + "XYZ");
ASSERT_EQ(lines.size(), 1);
ASSERT_EQ(lines[0], base + "XYZ");

lines = WordWrap(base + " XYZ");
ASSERT_EQ(lines.size(), 2);
ASSERT_EQ(lines[0], base);
ASSERT_EQ(lines[1], "XYZ");
}

TEST(WordWrapTest, limits) {
const std::string nstr = "0123456789";
const std::string limit = nstr + nstr + nstr + nstr + nstr;

auto lines = WordWrap(limit);
ASSERT_EQ(lines.size(), 1);
ASSERT_EQ(lines[0], limit);

auto test = limit + "X";
lines = WordWrap(test);
ASSERT_EQ(lines.size(), 1);
ASSERT_EQ(lines[0], test);

test = limit + limit;
lines = WordWrap(test);
ASSERT_EQ(lines.size(), 1);
ASSERT_EQ(lines[0], test);

test = limit + " " + limit;
lines = WordWrap(test);
ASSERT_EQ(lines.size(), 2);
ASSERT_EQ(lines[0], limit);
ASSERT_EQ(lines[1], limit);

test = limit + " " + limit;
lines = WordWrap(test);
ASSERT_EQ(lines.size(), 2);
ASSERT_EQ(lines[0], limit);
ASSERT_EQ(lines[1], limit);

test = limit.substr(0, limit.size()-1) + " " + limit;
lines = WordWrap(test);
ASSERT_EQ(lines.size(), 2);
ASSERT_EQ(lines[0], limit.substr(0, limit.size()-1));
ASSERT_EQ(lines[1], limit);

}

0 comments on commit e307071

Please sign in to comment.