diff --git a/utils/Color b/utils/Color new file mode 100644 index 0000000..94c0e24 Binary files /dev/null and b/utils/Color differ diff --git a/utils/Color.h b/utils/Color.h new file mode 100644 index 0000000..51b4807 --- /dev/null +++ b/utils/Color.h @@ -0,0 +1,128 @@ +#ifndef CONSTS_H +#define CONSTS_H + +#include +#include + +/* +FOREGROUND_BLUE 文本颜色包含蓝色。 +FOREGROUND_GREEN 文本颜色包含绿色。 +FOREGROUND_RED 文本颜色包含红色。 +FOREGROUND_INTENSITY 文本颜色增强。 +BACKGROUND_BLUE 背景色包含蓝色。 +BACKGROUND_GREEN 背景色包含绿色。 +BACKGROUND_RED 背景色包含红色。 +BACKGROUND_INTENSITY 背景色增强。 +*/ +enum { + COLOR_BLACK = 0, + COLOR_BLUE = 1, + COLOR_GREEN = 2, + COLOR_CYAN = 3, + COLOR_RED = 4, + COLOR_PURPLE = 5, + COLOR_YELLOW = 6, + COLOR_WHITE = 7, + COLOR_GRAY = 8, + COLOR_LIGHTBLUE = 9, + COLOR_LIGHTGREEN = 10, + COLOR_LIGHTCYAN = 11, + COLOR_LIGHTRED = 12, + COLOR_LIGHTPURPLE = 13, + COLOR_LIGHTYELLOW = 14, + COLOR_BRIGHTWHITE = 15 +}; + +typedef short MColor; + +inline MColor getColor(int front_color, int back_color) { + return (back_color << 4) | front_color; +} + +inline WORD FrontColorToWinColor(int color) { + switch (color) { + case COLOR_BLACK: + return 0; + case COLOR_BLUE: + return FOREGROUND_BLUE; + case COLOR_GREEN: + return FOREGROUND_GREEN; + case COLOR_CYAN: + return FOREGROUND_GREEN | FOREGROUND_BLUE; + case COLOR_RED: + return FOREGROUND_RED; + case COLOR_PURPLE: + return FOREGROUND_RED | FOREGROUND_BLUE; + case COLOR_YELLOW: + return FOREGROUND_RED | FOREGROUND_GREEN; + case COLOR_WHITE: + return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + case COLOR_GRAY: + return FOREGROUND_INTENSITY; + case COLOR_LIGHTBLUE: + return FOREGROUND_INTENSITY | FOREGROUND_BLUE; + case COLOR_LIGHTGREEN: + return FOREGROUND_INTENSITY | FOREGROUND_GREEN; + case COLOR_LIGHTCYAN: + return FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE; + case COLOR_LIGHTRED: + return FOREGROUND_INTENSITY | FOREGROUND_RED; + case COLOR_LIGHTPURPLE: + return FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE; + case COLOR_LIGHTYELLOW: + return FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN; + case COLOR_BRIGHTWHITE: + return FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + default: + return 0; + } +} + +inline int getFrontColor(MColor color) { + return color & 0x0F; +} + +inline WORD BackgroundColorToWinColor(int color) { + switch (color) { + case COLOR_BLACK: + return 0; + case COLOR_BLUE: + return BACKGROUND_BLUE; + case COLOR_GREEN: + return BACKGROUND_GREEN; + case COLOR_CYAN: + return BACKGROUND_GREEN | BACKGROUND_BLUE; + case COLOR_RED: + return BACKGROUND_RED; + case COLOR_PURPLE: + return BACKGROUND_RED | BACKGROUND_BLUE; + case COLOR_YELLOW: + return BACKGROUND_RED | BACKGROUND_GREEN; + case COLOR_WHITE: + return BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; + case COLOR_GRAY: + return BACKGROUND_INTENSITY; + case COLOR_LIGHTBLUE: + return BACKGROUND_INTENSITY | BACKGROUND_BLUE; + case COLOR_LIGHTGREEN: + return BACKGROUND_INTENSITY | BACKGROUND_GREEN; + case COLOR_LIGHTCYAN: + return BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_BLUE; + case COLOR_LIGHTRED: + return BACKGROUND_INTENSITY | BACKGROUND_RED; + case COLOR_LIGHTPURPLE: + return BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE; + case COLOR_LIGHTYELLOW: + return BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN; + case COLOR_BRIGHTWHITE: + return BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; + default: + return 0; + } +} + +inline int getBackColor(MColor color) { + return color & 0xF0; +} + +#endif // CONSTS_H \ No newline at end of file diff --git a/utils/RichText.h b/utils/RichText.h new file mode 100644 index 0000000..a548822 --- /dev/null +++ b/utils/RichText.h @@ -0,0 +1,149 @@ +#ifndef RICHTEXT_H +#define RICHTEXT_H + +#include "Color.h" +#include +#include +#include +#include +#include +#include + +struct StringPart { + std::string text; + MColor color; + + StringPart(const std::string& t, const MColor& c) : text(t), color(c) {} + StringPart(const std::string& t) : text(t), color(getColor(COLOR_WHITE, COLOR_BLACK)) {} +}; + +class RichText { +private: + std::vector parts; + + void print() const { + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + for(const auto& part : parts) { + std::cout << "[" << part.color << "] "; + SetConsoleTextAttribute(handle, BackgroundColorToWinColor(getBackColor(part.color)) | FrontColorToWinColor(getFrontColor(part.color))); + std::cout << part.text; + SetConsoleTextAttribute(handle, BackgroundColorToWinColor(COLOR_BLACK) | FrontColorToWinColor(COLOR_WHITE)); + std::cout << std::endl; + } + } + + void clearEmptyParts() { + parts.erase(std::remove_if(parts.begin(), parts.end(), [](const StringPart& part) { + return part.text.empty(); + }), parts.end()); + } + +public: + // 默认构造函数,初始化为一段默认黑底白字的文本 + RichText() { + parts.push_back({""}); + } + + // 重载运算符= + RichText& operator=(const RichText& other) { + parts = other.parts; + clearEmptyParts(); + return *this; + } + RichText& operator=(const std::string& other) { + parts.clear(); + parts.push_back({other}); + clearEmptyParts(); + return *this; + } + RichText& operator=(const StringPart& other) { + parts.clear(); + parts.push_back(other); + clearEmptyParts(); + return *this; + } + + // 重载运算符+ + RichText operator+(const RichText& other) const { + RichText result = *this; + result.parts.insert(result.parts.end(), other.parts.begin(), other.parts.end()); + result.clearEmptyParts(); + return result; + } + RichText operator+(const std::string& other) const { + RichText result = *this; + result.parts.push_back({other}); + result.clearEmptyParts(); + return result; + } + RichText operator+(const StringPart& other) const { + RichText result = *this; + result.parts.push_back(other); + result.clearEmptyParts(); + return result; + } + + // 重载运算符+= + RichText& operator+=(const RichText& other) { + parts.insert(parts.end(), other.parts.begin(), other.parts.end()); + clearEmptyParts(); + return *this; + } + RichText& operator+=(const std::string& other) { + parts.push_back({other}); + clearEmptyParts(); + return *this; + } + RichText& operator+=(const StringPart& other) { + parts.push_back(other); + clearEmptyParts(); + return *this; + } + + // substr()函数,保留原颜色 + RichText substr(size_t start, size_t length) const { + RichText result; + int s = 0, i = 0; + while(s + parts[i].text.length() < start) { + s += parts[i].text.length(); + i++; + } + int t_start = start - s; + int t_length = length; + while(t_length > 0 && i < parts.size()) { + if(t_start > 0) { + result += StringPart(parts[i].text.substr(t_start), parts[i].color); + t_length -= parts[i].text.length() - t_start; + t_start = 0; + } else { + if(t_length < parts[i].text.length()) { + result += StringPart(parts[i].text.substr(0, t_length), parts[i].color); + t_length = 0; + } else { + result += StringPart(parts[i].text, parts[i].color); + t_length -= parts[i].text.length(); + } + } + i++; + } + result.clearEmptyParts(); + // result.print(); + return result; + } + + std::vector getParts() const { + return parts; + } + + // plainText()函数,返回连接后的文本内容,不包括颜色 + std::string plainText() const { + std::string result; + for (const auto& part : parts) { + result += part.text; + } + return result; + } +}; + + +#endif \ No newline at end of file diff --git a/utils/RichText_test.cpp b/utils/RichText_test.cpp new file mode 100644 index 0000000..cded1be --- /dev/null +++ b/utils/RichText_test.cpp @@ -0,0 +1,55 @@ +#include +#include +#include "RichText.h" + +int main() { + // 测试默认构造函数 + RichText rt1; + assert(rt1.plainText() == ""); + + // 测试赋值运算符= + RichText rt2; + rt2 = "Hello"; + assert(rt2.plainText() == "Hello"); + + RichText rt3; + rt3 = StringPart{"World", COLOR_RED}; + assert(rt3.plainText() == "World"); + + // 测试加法运算符+ + RichText rt4 = rt2 + rt3; + assert(rt4.plainText() == "HelloWorld"); + + RichText rt5 = rt2 + "World"; + assert(rt5.plainText() == "HelloWorld"); + + RichText rt6 = rt2 + StringPart{"World", COLOR_RED}; + assert(rt6.plainText() == "HelloWorld"); + + // 测试加法赋值运算符+= + rt2 += rt3; + assert(rt2.plainText() == "HelloWorld"); + + rt2 += "World"; + assert(rt2.plainText() == "HelloWorldWorld"); + + rt2 += StringPart{"World", COLOR_RED}; + assert(rt2.plainText() == "HelloWorldWorldWorld"); + + // 测试substr()函数 + RichText rt7 = rt2.substr(5, 5); + assert(rt7.plainText() == "World"); + assert(rt7.getParts()[0].color == COLOR_RED); + + // 测试text()函数 + RichText rt8 = rt2; + assert(rt8.plainText() == "HelloWorldWorldWorld"); + assert(rt8.getParts()[0].color == COLOR_WHITE); + + // 测试plainText()函数 + std::string plain = rt2.plainText(); + assert(plain == "HelloWorldWorldWorld"); + + printf("All tests passed.\n"); + return 0; +}