fix RichText.substr()

decouple Text and Rect from old components
This commit is contained in:
梦凌汐 2024-12-10 16:21:33 +08:00
parent 80a80ca58a
commit b952c340d3
10 changed files with 264 additions and 171 deletions

View File

@ -9,19 +9,46 @@ public:
BaseComponent(int top, int left, int width, int height) : top(top), left(left), width(width), height(height) {}; BaseComponent(int top, int left, int width, int height) : top(top), left(left), width(width), height(height) {};
virtual ~BaseComponent() {}; virtual ~BaseComponent() {};
void setTop(int top); void setTop(int top) {
void setLeft(int left); this->top = top;
void setWidth(int width); }
void setHeight(int height); void setLeft(int left) {
this->left = left;
}
void setWidth(int width) {
this->width = width;
}
void setHeight(int height) {
this->height = height;
}
int getTop(); int getTop() {
int getLeft(); return top;
int getWidth(); }
int getHeight(); int getLeft() {
return left;
}
int getWidth() {
return width;
}
int getHeight() {
return height;
}
void setPosition(int top, int left); void setPosition(int top, int left) {
void setSize(int width, int height); this->top = top;
void setBounds(int top, int left, int width, int height); this->left = left;
}
void setSize(int width, int height) {
this->width = width;
this->height = height;
}
void setBounds(int top, int left, int width, int height) {
this->top = top;
this->left = left;
this->width = width;
this->height = height;
}
virtual void draw() = 0; virtual void draw() = 0;
virtual void onKeyPress(int key) = 0; virtual void onKeyPress(int key) = 0;

60
components/Rect.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef RECT_H
#define RECT_H
#include "BaseComponent.h"
#include "../utils/Color.h"
#include <windows.h>
#include <conio.h>
#include <stdio.h>
class Rect : public BaseComponent {
private:
MColor color_ = COLOR_WHITE;
public:
Rect(int top, int left, int width, int height) : BaseComponent(top, left, width, height){
color_ = getColor(COLOR_WHITE, COLOR_BLACK);
}
Rect(int top, int left, int width, int height, const MColor color) : BaseComponent(top, left, width, height){
color_ = color;
}
~Rect() override {}
void draw() override {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
//获得缓冲区信息
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hConsole, &csbi);
SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(getBackColor(color_)) | FrontColorToWinColor(getFrontColor(color_)));
for(int i = 1; i < height - 1 && top + i < csbi.dwSize.Y - 1; i++) {
if(left >= 0) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left - 1), static_cast<short>(top + i - 1)});
putchar('|');
}
if(left + width - 1 < csbi.dwSize.X) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + width - 2), static_cast<short>(top + i - 1)});
putchar('|');
}
}
for(int i = 0; i < width && left + i < csbi.dwSize.X; i++) {
if(top >= 0) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + i - 1), static_cast<short>(top - 1)});
putchar('-');
}
if(top + height - 1 < csbi.dwSize.Y) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + i - 1), static_cast<short>(top + height - 2)});
putchar('-');
}
}
SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(COLOR_BLACK) | FrontColorToWinColor(COLOR_WHITE));
}
void onKeyPress(int key) override {
//不处理按键
}
};
#endif

117
components/Text.h Normal file
View File

@ -0,0 +1,117 @@
#ifndef TEXT_H
#define TEXT_H
#include "BaseComponent.h"
#include <windows.h>
#include <vector>
#include <conio.h>
#include "../utils/RichText.h"
class Text : public BaseComponent {
private:
RichText text_;
int viewLeft_ = 0, viewTop_ = 0;
std::vector<RichText> lines_;
int maxLineWidth_ = 0;
public:
Text(int top, int left, int width, int height) : BaseComponent(top, left, width, height) {
text_ = RichText();
}
Text(int top, int left, int width, int height, const RichText& text) : BaseComponent(top, left, width, height) {
text_ = text;
}
~Text() override {
lines_.clear();
}
void setText(const RichText& text) {
this->text_ = text;
lines_.clear();
RichText line;
maxLineWidth_ = 0;
for(auto part : text.getParts()) {
if(part.text.find('\n') == std::string::npos) {
line += part;
// printf("Add: %s\n", part.text.c_str());
} else {
size_t begin = 0;
while((begin = part.text.find('\n', begin)) != std::string::npos) {
line += StringPart(part.text.substr(0, begin), part.color);
// printf("Add: %s\n", part.text.substr(0, begin).c_str());
lines_.push_back(line);
if(maxLineWidth_ < line.length()) {
maxLineWidth_ = line.length();
}
// printf("Push: %s\n", line.plainText().c_str());
line = RichText();
part.text = part.text.substr(begin + 1);
// printf("Rest: %s\n", part.text.c_str());
}
line += part;
// printf("Add: %s\n", part.text.c_str());
}
}
if(line.length() > 0) {
lines_.push_back(line);
if(maxLineWidth_ < line.length()) {
maxLineWidth_ = line.length();
}
}
}
RichText getText() {
return text_;
}
void draw() override {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
//获得缓冲区信息
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hConsole, &csbi);
for(int i = viewTop_; i < viewTop_ + height && i < lines_.size(); i++) {
RichText drawTarget = lines_[i].substr(viewLeft_, std::min(width, csbi.dwSize.X - left));
SetConsoleCursorPosition(hConsole, {static_cast<short>(left - 1), static_cast<short>(top + i - viewTop_ - 1)});
for(auto part : drawTarget.getParts()) {
SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(getBackColor(part.color)) | FrontColorToWinColor(getFrontColor(part.color)));
printf("%s", part.text.c_str());
}
CONSOLE_SCREEN_BUFFER_INFO currentCSBI;
GetConsoleScreenBufferInfo(hConsole, &currentCSBI);
for(int cx = currentCSBI.dwCursorPosition.X; cx < left + width - 1; cx++) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(cx), static_cast<short>(currentCSBI.dwCursorPosition.Y)});
printf(" ");
}
}
}
void setViewTop(int vtop) {
this->viewTop_ = vtop;
}
int getViewTop() {
return this->viewTop_;
}
void setViewLeft(int vleft) {
this->viewLeft_ = vleft;
}
int getViewLeft() {
return this->viewLeft_;
}
int getMaxLineWidth() {
return this->maxLineWidth_;
}
int getMaxLineCount() {
return this->lines_.size();
}
void onKeyPress(int key) override {}
};
#endif // TEXT_H

BIN
components/TextArea Normal file

Binary file not shown.

View File

@ -6,169 +6,73 @@
#include <vector> #include <vector>
#include <conio.h> #include <conio.h>
#include "../utils/RichText.h" #include "../utils/RichText.h"
#include "Rect.h"
#include "Text.h"
class TextArea : public BaseComponent { class TextArea : public BaseComponent {
private: private:
RichText text; Text text_ = Text(0, 0, 0, 0);
int viewLeft = 0, viewTop = 0; RichText title_;
std::vector<RichText> lines; Rect border_ = Rect(0, 0, 0, 0);
int maxLineWidth = 0;
RichText title;
int findnth(std::string str, std::string target, int n){
if(n == 0) return -1;
int count = 0;
size_t begin = 0;
while((begin = str.find(target, begin)) != std::string::npos){
count++;
begin += target.length();
if(n == count){
return begin - 1;
}
}
return -2;
}
public: public:
void setText(const RichText& text) {
this->text = text;
lines.clear();
RichText line;
maxLineWidth = 0;
for(auto part : text.getParts()) {
if(part.text.find('\n') == std::string::npos) {
line += part;
// printf("Add: %s\n", part.text.c_str());
} else {
size_t begin = 0;
while((begin = part.text.find('\n', begin)) != std::string::npos) {
line += StringPart(part.text.substr(0, begin), part.color);
// printf("Add: %s\n", part.text.substr(0, begin).c_str());
lines.push_back(line);
if(maxLineWidth < line.length()) {
maxLineWidth = line.length();
}
// printf("Push: %s\n", line.plainText().c_str());
line = RichText();
part.text = part.text.substr(begin + 1);
// printf("Rest: %s\n", part.text.c_str());
}
line += part;
// printf("Add: %s\n", part.text.c_str());
}
}
if(line.length() > 0) {
lines.push_back(line);
if(maxLineWidth < line.length()) {
maxLineWidth = line.length();
}
}
}
RichText getText() {
return text;
}
TextArea(int top, int left, int width, int height) : BaseComponent(top, left, width, height){ TextArea(int top, int left, int width, int height) : BaseComponent(top, left, width, height){
setText(RichText()); border_ = Rect(left, top, width, height);
text_ = Text(left + 1, top + 1, width - 2, height - 2);
text_.setText(RichText());
} }
TextArea(int top, int left, int width, int height, const RichText& text) : BaseComponent(top, left, width, height){ TextArea(int top, int left, int width, int height, const RichText& text) : BaseComponent(top, left, width, height){
setText(text); border_ = Rect(left, top, width, height);
text_ = Text(left + 1, top + 1, width - 2, height - 2);
text_.setText(text);
} }
~TextArea() override {} ~TextArea() override {}
void draw() override { void draw() override {
border_.draw();
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
//获得缓冲区信息 //获得缓冲区信息
CONSOLE_SCREEN_BUFFER_INFO csbi; CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hConsole, &csbi); GetConsoleScreenBufferInfo(hConsole, &csbi);
//清空区域
for(int i = 0; i < height && top + i < csbi.dwSize.Y; i++) {
for(int j = 0; j < width && left + j < csbi.dwSize.X; j++) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + j), static_cast<short>(top + i)});
putchar(' ');
}
}
//绘制边框到缓冲区
for(int i = 1; i < height - 1 && top + i < csbi.dwSize.Y - 1; i++) {
if(left >= 0) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left - 1), static_cast<short>(top + i - 1)});
putchar('|');
}
if(left + width - 1 < csbi.dwSize.X) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + width - 2), static_cast<short>(top + i - 1)});
putchar('|');
}
}
for(int i = 0; i < width && left + i < csbi.dwSize.X; i++) {
if(top >= 0) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + i - 1), static_cast<short>(top - 1)});
putchar('-');
}
if(top + height - 1 < csbi.dwSize.Y) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + i - 1), static_cast<short>(top + height - 2)});
putchar('-');
}
}
//绘制标题 //绘制标题
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + 1), static_cast<short>(top - 1)}); SetConsoleCursorPosition(hConsole, {static_cast<short>(left + 1), static_cast<short>(top - 1)});
for(auto part : title.getParts()) { for(auto part : title_.getParts()) {
SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(getBackColor(part.color)) | FrontColorToWinColor(getFrontColor(part.color))); SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(getBackColor(part.color)) | FrontColorToWinColor(getFrontColor(part.color)));
printf("%s", part.text.c_str()); printf("%s", part.text.c_str());
} }
for(int i = viewTop; i < viewTop + height - 2 && i < lines.size(); i++) { text_.draw();
RichText drawTarget = lines[i].substr(viewLeft, std::min(width - 2, csbi.dwSize.X - left - 2));
SetConsoleCursorPosition(hConsole, {static_cast<short>(left), static_cast<short>(top + i - viewTop)});
for(auto part : drawTarget.getParts()) {
SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(getBackColor(part.color)) | FrontColorToWinColor(getFrontColor(part.color)));
printf("%s", part.text.c_str());
}
}
SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(COLOR_BLACK) | FrontColorToWinColor(COLOR_WHITE)); SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(COLOR_BLACK) | FrontColorToWinColor(COLOR_WHITE));
} }
void setViewTop(int top) { void setText(const RichText& newText) {
this->viewTop = top; text_.setText(newText);
}
int getViewTop() {
return this->viewTop;
}
void setViewLeft(int left) {
this->viewLeft = left;
}
int getViewLeft() {
return this->viewLeft;
} }
void moveLeft() { void moveLeft() {
if(viewLeft > 0) { if(text_.getViewLeft() > 0) {
viewLeft--; text_.setViewLeft(text_.getViewLeft() - 1);
} }
} }
void moveRight() { void moveRight() {
if(viewLeft < maxLineWidth - width + 2) { if(text_.getViewLeft() < text_.getMaxLineWidth() - width + 2) {
viewLeft++; text_.setViewLeft(text_.getViewLeft() + 1);
} }
} }
void moveUp() { void moveUp() {
if(viewTop > 0) { if(text_.getViewTop() > 0) {
viewTop--; text_.setViewTop(text_.getViewTop() - 1);
} }
} }
void moveDown() { void moveDown() {
if(viewTop < lines.size() - height + 2) { if(text_.getViewTop() < text_.getMaxLineCount() - height + 2) {
viewTop++; text_.setViewTop(text_.getViewTop() + 1);
} }
} }
@ -176,8 +80,8 @@ public:
//不处理按键 //不处理按键
} }
void setTitle(const RichText& title) { void setTitle(const RichText& newTitle) {
this->title = title; this->title_ = newTitle;
} }
}; };

View File

@ -12,7 +12,7 @@ int main() {
richText += " And more more more more more text.\n"; richText += " And more more more more more text.\n";
richText += StringPart("And more more more more more more text.", COLOR_BLUE); richText += StringPart("And more more more more more more text.", COLOR_BLUE);
richText += " And more more more more more more more text.\n"; richText += " And more more more more more more more text.\n";
richText += StringPart("And more more more more more more more more text.\n", COLOR_YELLOW); richText += StringPart(" With some spaces before And more more more more more more more more text.\n", COLOR_YELLOW);
richText += "And more more more more more more more more more text.\n"; richText += "And more more more more more more more more more text.\n";
richText += StringPart("And more more more more more more more more more more text.\n", COLOR_CYAN); richText += StringPart("And more more more more more more more more more more text.\n", COLOR_CYAN);

View File

@ -4,63 +4,43 @@
#include "BaseComponent.h" #include "BaseComponent.h"
#include <windows.h> #include <windows.h>
#include "../utils/RichText.h" #include "../utils/RichText.h"
#include "Rect.h"
#include "Text.h"
class TextLine : public BaseComponent { class TextLine : public BaseComponent {
private: private:
RichText text; Text text_ = Text(0, 0, 0, 0);
Rect border_ = Rect(0, 0, 0, 0);
public: public:
TextLine(int top, int left, int width, int height) : BaseComponent(top, left, width, height){ TextLine(int top, int left, int width, int height) : BaseComponent(top, left, width, height){
text = RichText(); text_ = Text(top + 1, left + 1, width - 2, 1);
border_ = Rect(top, left, width, height);
} }
TextLine(int top, int left, int width, int height, const RichText& text) : BaseComponent(top, left, width, height){ TextLine(int top, int left, int width, int height, const RichText& text) : BaseComponent(top, left, width, height){
this->text = text; text_ = Text(top + 1, left + 1, width - 2, 1, text);
border_ = Rect(top, left, width, height);
} }
~TextLine() override {} ~TextLine() override {}
void setText(const RichText& text) { void setText(const RichText& text) {
this->text = text; text_.setText(text);
} }
RichText getText() { RichText getText() {
return text; return text_.getText();
} }
void draw() override { void draw() override {
border_.draw();
//使用渲染边框,超出屏幕边缘的区域不渲染 //使用渲染边框,超出屏幕边缘的区域不渲染
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
//获得缓冲区信息 //获得缓冲区信息
CONSOLE_SCREEN_BUFFER_INFO csbi; CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hConsole, &csbi); GetConsoleScreenBufferInfo(hConsole, &csbi);
//绘制边框到缓冲区
for(int i = 0; i < height && top + i < csbi.dwSize.Y; i++) { text_.draw();
if(left >= 0) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left - 1), static_cast<short>(top + i - 1)});
printf("|");
}
if(left + width - 1 < csbi.dwSize.X) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + width - 2), static_cast<short>(top + i - 1)});
printf("|");
}
}
for(int i = 0; i < width && left + i < csbi.dwSize.X; i++) {
if(top >= 0) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + i - 1), static_cast<short>(top - 1)});
printf("-");
}
if(top + height - 1 < csbi.dwSize.Y) {
SetConsoleCursorPosition(hConsole, {static_cast<short>(left + i - 1), static_cast<short>(top + height - 2)});
printf("-");
}
}
//绘制文本到缓冲区,保证不超出边框
RichText drawTarget = text.substr(0, std::min(width - 2, csbi.dwSize.X - left - 2));
SetConsoleCursorPosition(hConsole, {static_cast<short>(left), static_cast<short>(top)});
for(auto part : drawTarget.getParts()) {
SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(getBackColor(part.color)) | FrontColorToWinColor(getFrontColor(part.color)));
printf("%s", part.text.c_str());
}
SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(COLOR_BLACK) | FrontColorToWinColor(COLOR_WHITE)); SetConsoleTextAttribute(hConsole, BackgroundColorToWinColor(COLOR_BLACK) | FrontColorToWinColor(COLOR_WHITE));
} }

View File

@ -14,7 +14,8 @@ BACKGROUND_GREEN 背景色包含绿色。
BACKGROUND_RED BACKGROUND_RED
BACKGROUND_INTENSITY BACKGROUND_INTENSITY
*/ */
enum {
enum Colors {
COLOR_BLACK = 0, COLOR_BLACK = 0,
COLOR_BLUE = 1, COLOR_BLUE = 1,
COLOR_GREEN = 2, COLOR_GREEN = 2,

View File

@ -124,7 +124,7 @@ public:
int t_length = length; int t_length = length;
while(t_length > 0 && i < parts.size()) { while(t_length > 0 && i < parts.size()) {
if(t_start > 0) { if(t_start > 0) {
result += StringPart(parts[i].text.substr(t_start), parts[i].color); result += StringPart(parts[i].text.substr(t_start, std::min(static_cast<size_t>(t_length), parts[i].text.length() - t_start)), parts[i].color);
t_length -= parts[i].text.length() - t_start; t_length -= parts[i].text.length() - t_start;
t_start = 0; t_start = 0;
} else { } else {

View File

@ -50,6 +50,10 @@ int main() {
std::string plain = rt2.plainText(); std::string plain = rt2.plainText();
assert(plain == "HelloWorldWorldWorld"); assert(plain == "HelloWorldWorldWorld");
RichText rt9 = RichText(" With some spaces before and after ");
RichText rt10 = rt9.substr(5, 20);
assert(rt10.plainText() == "ith some spaces befo");
printf("All tests passed.\n"); printf("All tests passed.\n");
return 0; return 0;
} }