Prefer std::string over std::wstring. (#179)

In the past, FTXUI switched from std::string to std::wstring to support
fullwidth characters. The reasons was that fullwidth characters can be
stored inside a single wchar_t.

Then FTXUI added support for combining characters. A single glygh
doesn't even fit a wchar_t. Instead, a glyph can be arbitrary large.

The usage of wstring doesn't really fit the new model and have several
drawbacks:
1. It doesn't simplify the implementation of FTXUI, because of combining
   characters.
2. It reduces drawing performance by 2x.
3. It increase Screen's memory allocation by 2x.

This patch converts FTXUI to use std::string internally. It now exposes
std::string based API. The std::wstring API remains, but is now
deprecated.

Tests and examples haven't been update to show the breakage is limited.
They will be updated in a second set of patches.

Bug: https://github.com/ArthurSonzogni/FTXUI/issues/153
Co-authored-by: Tushar Maheshwari <tushar27192@gmail.com>
This commit is contained in:
Arthur Sonzogni
2021-08-08 23:25:20 +02:00
committed by GitHub
parent 1ff894f6f5
commit 3b4ab618a3
84 changed files with 1234 additions and 996 deletions

View File

@@ -1,10 +1,9 @@
#include <algorithm> // for max
#include <iostream> // for operator<<, basic_ostream, wstringstream, stringstream, flush, cout, ostream
#include <memory> // for allocator
#include <sstream> // IWYU pragma: keep
#include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream
#include <memory> // for allocator
#include <sstream> // IWYU pragma: keep
#include "ftxui/screen/screen.hpp"
#include "ftxui/screen/string.hpp" // for to_string, wchar_width
#include "ftxui/screen/string.hpp" // for string_width
#include "ftxui/screen/terminal.hpp" // for Dimensions, Size
#if defined(_WIN32)
@@ -18,24 +17,24 @@
namespace ftxui {
namespace {
static const wchar_t* BOLD_SET = L"\x1B[1m";
static const wchar_t* BOLD_RESET = L"\x1B[22m"; // Can't use 21 here.
static const char BOLD_SET[] = "\x1B[1m";
static const char BOLD_RESET[] = "\x1B[22m"; // Can't use 21 here.
static const wchar_t* DIM_SET = L"\x1B[2m";
static const wchar_t* DIM_RESET = L"\x1B[22m";
static const char DIM_SET[] = "\x1B[2m";
static const char DIM_RESET[] = "\x1B[22m";
static const wchar_t* UNDERLINED_SET = L"\x1B[4m";
static const wchar_t* UNDERLINED_RESET = L"\x1B[24m";
static const char UNDERLINED_SET[] = "\x1B[4m";
static const char UNDERLINED_RESET[] = "\x1B[24m";
static const wchar_t* BLINK_SET = L"\x1B[5m";
static const wchar_t* BLINK_RESET = L"\x1B[25m";
static const char BLINK_SET[] = "\x1B[5m";
static const char BLINK_RESET[] = "\x1B[25m";
static const wchar_t* INVERTED_SET = L"\x1B[7m";
static const wchar_t* INVERTED_RESET = L"\x1B[27m";
static const char INVERTED_SET[] = "\x1B[7m";
static const char INVERTED_RESET[] = "\x1B[27m";
static const char* MOVE_LEFT = "\r";
static const char* MOVE_UP = "\x1B[1A";
static const char* CLEAR_LINE = "\x1B[2K";
static const char MOVE_LEFT[] = "\r";
static const char MOVE_UP[] = "\x1B[1A";
static const char CLEAR_LINE[] = "\x1B[2K";
Pixel dev_null_pixel;
@@ -62,7 +61,9 @@ void WindowsEmulateVT100Terminal() {
}
#endif
void UpdatePixelStyle(std::wstringstream& ss, Pixel& previous, Pixel& next) {
void UpdatePixelStyle(std::stringstream& ss,
Pixel& previous,
const Pixel& next) {
if (next.bold != previous.bold)
ss << (next.bold ? BOLD_SET : BOLD_RESET);
@@ -80,8 +81,8 @@ void UpdatePixelStyle(std::wstringstream& ss, Pixel& previous, Pixel& next) {
if (next.foreground_color != previous.foreground_color ||
next.background_color != previous.background_color) {
ss << L"\x1B[" + next.foreground_color.Print(false) + L"m";
ss << L"\x1B[" + next.background_color.Print(true) + L"m";
ss << "\x1B[" + next.foreground_color.Print(false) + "m";
ss << "\x1B[" + next.background_color.Print(true) + "m";
}
previous = next;
@@ -135,7 +136,7 @@ Screen::Screen(int dimx, int dimy)
/// Produce a std::string that can be used to print the Screen on the terminal.
/// Don't forget to flush stdout. Alternatively, you can use Screen::Print();
std::string Screen::ToString() {
std::wstringstream ss;
std::stringstream ss;
Pixel previous_pixel;
Pixel final_pixel;
@@ -143,24 +144,21 @@ std::string Screen::ToString() {
for (int y = 0; y < dimy_; ++y) {
if (y != 0) {
UpdatePixelStyle(ss, previous_pixel, final_pixel);
ss << L"\r\n";
ss << "\r\n";
}
for (int x = 0; x < dimx_;) {
auto& pixel = pixels_[y][x];
UpdatePixelStyle(ss, previous_pixel, pixel);
int x_inc = 0;
for (auto& c : pixel.character) {
ss << c;
x_inc += wchar_width(c);
bool previous_fullwidth = false;
for (const auto& pixel : pixels_[y]) {
if (!previous_fullwidth) {
UpdatePixelStyle(ss, previous_pixel, pixel);
ss << pixel.character;
}
x += std::max(x_inc, 1);
previous_fullwidth = (string_width(pixel.character) == 2);
}
}
UpdatePixelStyle(ss, previous_pixel, final_pixel);
return to_string(ss.str());
return ss.str();
}
void Screen::Print() {
@@ -170,8 +168,8 @@ void Screen::Print() {
/// @brief Access a character a given position.
/// @param x The character position along the x-axis.
/// @param y The character position along the y-axis.
wchar_t& Screen::at(int x, int y) {
return PixelAt(x, y).character[0];
std::string& Screen::at(int x, int y) {
return PixelAt(x, y).character;
}
/// @brief Access a Pixel at a given position.
@@ -229,21 +227,21 @@ void Screen::ApplyShader() {
// Merge box characters togethers.
for (int y = 1; y < dimy_; ++y) {
for (int x = 1; x < dimx_; ++x) {
wchar_t& left = at(x - 1, y);
wchar_t& top = at(x, y - 1);
wchar_t& cur = at(x, y);
std::string& left = at(x - 1, y);
std::string& top = at(x, y - 1);
std::string& cur = at(x, y);
// Left vs current
if (cur == U'' && left == U'') cur = U'';
if (cur == U'' && left == U'') left = U'';
if (cur == U'' && left == U'') cur = U'';
if (cur == U'' && left == U'') left = U'';
if (cur == "" && left == "") cur = "";
if (cur == "" && left == "") left = "";
if (cur == "" && left == "") cur = "";
if (cur == "" && left == "") left = "";
// Top vs current
if (cur == U'' && top == U'') cur = U'';
if (cur == U'' && top == U'') top = U'';
if (cur == U'' && top == U'') cur = U'';
if (cur == U'' && top == U'') top = U'';
if (cur == "" && top == "") cur = "";
if (cur == "" && top == "") top = "";
if (cur == "" && top == "") cur = "";
if (cur == "" && top == "") top = "";
}
}
}