User-Friendly Terminal Output in C++

  • By Michael Bryant (Shadow53)
  • 2 min read

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.