mirror of
https://github.com/MeowLynxSea/ceditor.git
synced 2025-07-09 10:54:37 +00:00
Add RichText class to support string with colors.
This commit is contained in:
parent
89b5045321
commit
6ba9a3e1eb
BIN
utils/Color
Normal file
BIN
utils/Color
Normal file
Binary file not shown.
128
utils/Color.h
Normal file
128
utils/Color.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#ifndef CONSTS_H
|
||||||
|
#define CONSTS_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <wincon.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
149
utils/RichText.h
Normal file
149
utils/RichText.h
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#ifndef RICHTEXT_H
|
||||||
|
#define RICHTEXT_H
|
||||||
|
|
||||||
|
#include "Color.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
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<StringPart> 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<StringPart> getParts() const {
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// plainText()函数,返回连接后的文本内容,不包括颜色
|
||||||
|
std::string plainText() const {
|
||||||
|
std::string result;
|
||||||
|
for (const auto& part : parts) {
|
||||||
|
result += part.text;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
55
utils/RichText_test.cpp
Normal file
55
utils/RichText_test.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user