User-Friendly Terminal Output in C++
In the course of creating my RPG engine, I needed to create a command-line interface - both for the final product and so I can test my engine's code. Terminal output is annoying in any language, because the standard output doesn't care about where your words break or if the output has long since overflowed off the screen. Half of the time you'll have some word break in the middle between lines, which really isn't great for a user's experience. So, I've been working on a solution that produces output that's a bit more readable. In its current state it only checks if there will be an overflow and, if so, pauses output and prompts the user to "press enter to continue". Next up is figuring out the breaks between words and breaking on those instead so that a portion of a paragraph can be printed.
The Source
// For string support
#include <string>
// For printing to cout
#include <iostream>
// The stringstream is what we use to parse the string
#include <sstream>
static size_t rowsPrinted{0}
static size_t colsPrinted{0}
void pauseThenContinue() {
println("[Press enter to continue]");
std::cin.ignore();
// clear() is system-dependent
// See http://www.cplusplus.com/articles/4z18T05o/ for discussion/implementation
clear();
}
void CMDInterface::print(std::string str) {
// Create a stream and initialize it to the given string
std::stringstream stream;
stream.str(str);
// Create an empty variable to hold the substring
std::string substr;
// Will hold how many rows and characters in the last row
// that the current substring will take up
size_t rows{0};
size_t cols{0};
// Will hold the length of the current substring
size_t len;
// Will temporarily hold the number of columns and rows in the window
size_t termCols;
size_t termRows;
// Current number of characters written over course of all substirngs:
// for ensuring no trailing newline
size_t printedLen{0};
// getline breaks the stream in sections by the newline character
// each substring is a paragraph
while (getline(stream, substr, '\n')) {
// Cache values for quicker access later
len = substr.length();
termCols = getCols();
termRows = getRows();
// Calculate how many rows and columns in the final row
// that the string will take up
// Rows will be 0 if length is fewer than cols
rows += len / termCols;
cols += len % termCols;
// Decide whether to clear before printing
// + 2 accounts for the prompt to continue and newline after
if (rowsPrinted + rows + 2 > termRows) {
pauseThenContinue();
// The
rowsPrinted = 0;
colsPrinted = 0;
}
// Remember will be printed
rowsPrinted += rows;
colsPrinted += cols;
// Actually print
std::cout << substr;
// Add substring's length to how much of the string got printed
printedLen += len;
// \n got removed, so add it back in if there is more of the string to be printed
if (printedLen < str.length()) {
std::cout << '\n';
colsPrinted = 0;
printedLen += 1;
rowsPrinted += 1;
}
}
// This process continues for the whole string
}In its current implementation, words will split onto the next line as soon as the output reaches the right side of the terminal. Th
is stinks. It's really annoying to hav
e text split randomly.
Anyways, that is currently on the list of things to get done before I can consider this implementation to be complete. I thought I would share my findings though with the working prototype.