From 1678943d9d9ea955b64f8597f875bbf559b243ff Mon Sep 17 00:00:00 2001 From: csboo Date: Fri, 31 May 2024 12:21:36 +0200 Subject: [PATCH 1/3] cute boxe commit --- test.cpp | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 test.cpp diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..8381bfe --- /dev/null +++ b/test.cpp @@ -0,0 +1,183 @@ +#include "tui.hpp" +#include +#include +#include +#include +#include +#include +#include + +using namespace tui::text::style; +using namespace tui::text::color; + +namespace tui { + + class Box { + public: + enum class Style { Empty, Light, Heavy, Double, Dashed, Rounded }; + enum class Align { TopLeft, TopRight, BottomLeft, BottomRight, Center, Custom }; + Box(int width, int height, Style style = Style::Empty, std::string title = "") + : width(width), height(height), title(std::move(title)), style(style) { + this->content.resize((width+1)*(height+1)); + std::cerr << content.size()<< "resize done\n"; + // for (auto & i : content) { + // i = "X"; + // } + init_box(); + } + + // void add_text(const std::string& text, Align alignment = Align::TopLeft, int x = 0, int y = 0) { + // texts.push_back({text, alignment, x, y}); + // } + + void init_box() { + const Box_chars& chars = styles.at(style); + + // Top border + content.at(getij(0, 0)) = chars.corners[0]; + std::cerr << "topleft done\n"; + // std::cerr << getij(0,0) << "\n"; + + for(int j = 1; j < width; j++){ + content.at(getij(0, j)) = chars.horizontal; + } + std::cerr << "topdone\n"; + + content.at(getij(0, width)) = chars.corners[1]; + std::cerr << "topright done\n"; + + // Side borders + for (int i = 1; i < height; i++) { + content.at(getij(i, 0)) = chars.vertical; + } + for (int i = 1; i < height; i++) { + content.at(getij(i, width)) = chars.vertical; + } + std::cerr << "side done\n"; + // Bottom border + content.at(getij(height, 0)) = chars.corners[3]; + std::cerr << "bottom left done\n"; + for (int j = 1; j < width; j++ ) { + content.at(getij(height, j)) = chars.horizontal; + } + std::cerr << "bottom done\n"; + content.at(getij(height, width)) = chars.corners[2]; + std::cerr << "last done\n"; + }; + void print(){ + // for (int i = 0; i < width; i++) { + // for (int j = 0; j < height; j++) { + // std::cout << getij(i, j); + // } + // std::cout << "\n"; + // } + int c =0; + for (int i = 0; i < height+1; i++) { + for (int j = 0; j < width+1; j++ ) { + if (j==0) { + if (c==10) { + std::cout << 1; + c++; + } else { + std::cout << c++; + } + if (c==11){ + c = 1; + } + } + std::cout << content.at(getij(i,j)); + } + std::cout << "\n"; + } + } + + inline int get_width() const {return this->width;} + + inline int getij(int i, int j) const { + return j + i * get_width(); + }; + private: + struct Box_chars { + std::string corners[4]; + std::string horizontal; + std::string vertical; + }; + + struct Text { + std::string content; + Align alignment; + int x; + int y; + }; + struct Coord{ + int x; + int y; + }; + + const int width; + const int height; + + Coord astart; + Coord aend; + Coord rstart; + Coord rend; + + std::string title; + + Style style; + std::vector texts; + const std::unordered_map styles = { + {Style::Light, {{"┌", "┐", "┘", "└"}, "─", "│"}}, + {Style::Heavy, {{"┏", "┓", "┛", "┗"}, "━", "┃"}}, + {Style::Double, {{"╔", "╗", "╝", "╚"}, "═", "║"}}, + {Style::Dashed, {{"┌", "┐", "┘", "└"}, "┄", "┆"}}, + {Style::Empty, {{" ", " ", " ", " "}, " ", " "}}, + {Style::Rounded, {{"╭", "╮", "╯", "╰"}, "─", "│"}}, + }; + + std::vector content; + + + // int get_align(Align alignment) const { + // switch (alignment) { + // case Align::TopLeft: + // case Align::TopRight: + // return 0; + // case Align::BottomLeft: + // case Align::BottomRight: + // return height - 3; + // case Align::Center: + // return (height - 2) / 2; + // default: + // return 0; + // } + // } + + // std::string get_text(int line, const Box_chars& chars) const { + // const Text& text = texts[0]; // Simplification: handle only the first text for now + // int padding = (width - 2 - text.content.size()) / 2; + // switch (text.alignment) { + // case Align::TopLeft: + // case Align::BottomLeft: + // return text.content + std::string(width - 2 - text.content.size(), ' '); + // case Align::TopRight: + // case Align::BottomRight: + // return std::string(width - 2 - text.content.size(), ' ') + text.content; + // case Align::Center: + // return std::string(padding, ' ') + text.content + + // std::string(width - 2 - padding - text.content.size(), ' '); + // default: + // return text.content; + // } + // } + }; +} // namespace tui + +int main() { + int width = 20; + int height = 10; + tui::Box alma(width, height, tui::Box::Style::Rounded); + std::cerr << "main box init done\n"; + alma.print(); + return 0; +} From a7d40b8230475e65a77c498d7b08b2350c5ecb29 Mon Sep 17 00:00:00 2001 From: csboo Date: Fri, 31 May 2024 17:32:22 +0200 Subject: [PATCH 2/3] switching to coords in progress --- test.cpp | 79 ++++++++++++-------------------------------------------- 1 file changed, 16 insertions(+), 63 deletions(-) diff --git a/test.cpp b/test.cpp index 8381bfe..8ac7400 100644 --- a/test.cpp +++ b/test.cpp @@ -18,11 +18,6 @@ namespace tui { enum class Align { TopLeft, TopRight, BottomLeft, BottomRight, Center, Custom }; Box(int width, int height, Style style = Style::Empty, std::string title = "") : width(width), height(height), title(std::move(title)), style(style) { - this->content.resize((width+1)*(height+1)); - std::cerr << content.size()<< "resize done\n"; - // for (auto & i : content) { - // i = "X"; - // } init_box(); } @@ -34,68 +29,20 @@ namespace tui { const Box_chars& chars = styles.at(style); // Top border - content.at(getij(0, 0)) = chars.corners[0]; - std::cerr << "topleft done\n"; - // std::cerr << getij(0,0) << "\n"; - - for(int j = 1; j < width; j++){ - content.at(getij(0, j)) = chars.horizontal; - } - std::cerr << "topdone\n"; - - content.at(getij(0, width)) = chars.corners[1]; - std::cerr << "topright done\n"; // Side borders - for (int i = 1; i < height; i++) { - content.at(getij(i, 0)) = chars.vertical; - } - for (int i = 1; i < height; i++) { - content.at(getij(i, width)) = chars.vertical; - } - std::cerr << "side done\n"; + // Bottom border - content.at(getij(height, 0)) = chars.corners[3]; - std::cerr << "bottom left done\n"; - for (int j = 1; j < width; j++ ) { - content.at(getij(height, j)) = chars.horizontal; - } - std::cerr << "bottom done\n"; - content.at(getij(height, width)) = chars.corners[2]; - std::cerr << "last done\n"; }; void print(){ - // for (int i = 0; i < width; i++) { - // for (int j = 0; j < height; j++) { - // std::cout << getij(i, j); - // } - // std::cout << "\n"; - // } - int c =0; - for (int i = 0; i < height+1; i++) { - for (int j = 0; j < width+1; j++ ) { - if (j==0) { - if (c==10) { - std::cout << 1; - c++; - } else { - std::cout << c++; - } - if (c==11){ - c = 1; - } - } - std::cout << content.at(getij(i,j)); - } - std::cout << "\n"; - } + } inline int get_width() const {return this->width;} + inline void set_width(unsigned width) {this->width = width;} + inline int get_height() const {return this->width;} + inline void set_height(unsigned height) {this->height = height;} - inline int getij(int i, int j) const { - return j + i * get_width(); - }; private: struct Box_chars { std::string corners[4]; @@ -109,13 +56,14 @@ namespace tui { int x; int y; }; + struct Coord{ int x; int y; }; - const int width; - const int height; + int width; + int height; Coord astart; Coord aend; @@ -134,9 +82,6 @@ namespace tui { {Style::Empty, {{" ", " ", " ", " "}, " ", " "}}, {Style::Rounded, {{"╭", "╮", "╯", "╰"}, "─", "│"}}, }; - - std::vector content; - // int get_align(Align alignment) const { // switch (alignment) { @@ -174,10 +119,18 @@ namespace tui { } // namespace tui int main() { + tui::init_term(true); + int width = 20; int height = 10; tui::Box alma(width, height, tui::Box::Style::Rounded); std::cerr << "main box init done\n"; alma.print(); + + char car = '\0'; + // tui::cursor::set_position(); + std::cout << "q to quit"; + while(car != 'q'){ std::cin.get(car); } + tui::reset_term(); return 0; } From 88656d534ae1b1332ccf6fe30534f5be84ff3a13 Mon Sep 17 00:00:00 2001 From: csboo Date: Sat, 1 Jun 2024 19:47:18 +0200 Subject: [PATCH 3/3] using latest tui.hpp, small fix --- test.cpp | 67 ++++++++++++---------- tui.hpp | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 203 insertions(+), 35 deletions(-) diff --git a/test.cpp b/test.cpp index 8ac7400..bea97fa 100644 --- a/test.cpp +++ b/test.cpp @@ -22,26 +22,24 @@ namespace tui { } // void add_text(const std::string& text, Align alignment = Align::TopLeft, int x = 0, int y = 0) { - // texts.push_back({text, alignment, x, y}); + // texts.push_back({text, alignment, x, y}); // } void init_box() { const Box_chars& chars = styles.at(style); // Top border - + // Side borders // Bottom border }; - void print(){ - - } + void print() {} - inline int get_width() const {return this->width;} - inline void set_width(unsigned width) {this->width = width;} - inline int get_height() const {return this->width;} - inline void set_height(unsigned height) {this->height = height;} + inline int get_width() const { return this->width; } + inline void set_width(unsigned width) { this->width = width; } + inline int get_height() const { return this->width; } + inline void set_height(unsigned height) { this->height = height; } private: struct Box_chars { @@ -57,7 +55,7 @@ namespace tui { int y; }; - struct Coord{ + struct Coord { int x; int y; }; @@ -69,18 +67,15 @@ namespace tui { Coord aend; Coord rstart; Coord rend; - + std::string title; Style style; std::vector texts; const std::unordered_map styles = { - {Style::Light, {{"┌", "┐", "┘", "└"}, "─", "│"}}, - {Style::Heavy, {{"┏", "┓", "┛", "┗"}, "━", "┃"}}, - {Style::Double, {{"╔", "╗", "╝", "╚"}, "═", "║"}}, - {Style::Dashed, {{"┌", "┐", "┘", "└"}, "┄", "┆"}}, - {Style::Empty, {{" ", " ", " ", " "}, " ", " "}}, - {Style::Rounded, {{"╭", "╮", "╯", "╰"}, "─", "│"}}, + {Style::Light, {{"┌", "┐", "┘", "└"}, "─", "│"}}, {Style::Heavy, {{"┏", "┓", "┛", "┗"}, "━", "┃"}}, + {Style::Double, {{"╔", "╗", "╝", "╚"}, "═", "║"}}, {Style::Dashed, {{"┌", "┐", "┘", "└"}, "┄", "┆"}}, + {Style::Empty, {{" ", " ", " ", " "}, " ", " "}}, {Style::Rounded, {{"╭", "╮", "╯", "╰"}, "─", "│"}}, }; // int get_align(Align alignment) const { @@ -118,19 +113,33 @@ namespace tui { }; } // namespace tui +void handle_resize(int) { + tui::screen::clear(); +} + int main() { - tui::init_term(true); - - int width = 20; - int height = 10; - tui::Box alma(width, height, tui::Box::Style::Rounded); - std::cerr << "main box init done\n"; - alma.print(); - - char car = '\0'; - // tui::cursor::set_position(); - std::cout << "q to quit"; - while(car != 'q'){ std::cin.get(car); } + tui::init_term(false); + tui::set_up_resize(handle_resize); + + try { + int width = 20; + int height = 10; + tui::Box alma(width, height, tui::Box::Style::Rounded); + std::cerr << "main box init done\n"; + alma.print(); + + char car = '\0'; + // tui::cursor::set_position(); + std::cout << "q to quit"; + while (car != 'q') { + std::cin.get(car); + } + } + catch (...) { + tui::reset_term(); + std::cout << "ran into a problem"; + return 1; + } tui::reset_term(); return 0; } diff --git a/tui.hpp b/tui.hpp index 1c66d41..106b288 100644 --- a/tui.hpp +++ b/tui.hpp @@ -3,7 +3,12 @@ #ifndef TUI_H #define TUI_H +#ifdef _WIN32 // windows +#include +#include +#endif #include +#include #include #include #include @@ -13,6 +18,92 @@ namespace tui { constexpr const char PURE_ESC = '\x1B'; // actually == "ESC[" almost everything starts with this constexpr const char* const ESC = "\x1B["; + + inline void enable_raw_mode() { +#ifdef _WIN32 + // Windows-specific code + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + if (hStdin == INVALID_HANDLE_VALUE) { + std::cerr << "Error getting the standard input handle." << std::endl; + exit(1); + } + + DWORD mode; + if (!GetConsoleMode(hStdin, &mode)) { + std::cerr << "Error getting the console mode." << std::endl; + exit(1); + } + + DWORD newMode = mode; + newMode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); + + if (!SetConsoleMode(hStdin, newMode)) { + std::cerr << "Error setting the console to raw mode." << std::endl; + exit(1); + } +#else + // Unix-like systems specific code + // struct termios term {}; + // if (tcgetattr(STDIN_FILENO, &term) == -1) { + // std::cerr << "Error getting terminal attributes." << std::endl; + // exit(1); + // } + + // struct termios raw = term; + // raw.c_lflag &= ~(ICANON | ECHO); + + // if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) { + // std::cerr << "Error setting terminal to raw mode." << std::endl; + // exit(1); + // } + + system("stty raw"); + system("stty -echo"); +#endif + } + + inline void disable_raw_mode() { +#ifdef _WIN32 + // Windows-specific code + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + if (hStdin == INVALID_HANDLE_VALUE) { + std::cerr << "Error getting the standard input handle." << std::endl; + exit(1); + } + + DWORD mode; + if (!GetConsoleMode(hStdin, &mode)) { + std::cerr << "Error getting the console mode." << std::endl; + exit(1); + } + + // Restore original mode + mode |= (ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); + if (!SetConsoleMode(hStdin, mode)) { + std::cerr << "Error restoring the console mode." << std::endl; + exit(1); + } +#else + // Unix-like systems specific code + system("stty -raw"); + system("stty echo"); + + // struct termios term {}; + // if (tcgetattr(STDIN_FILENO, &term) == -1) { + // std::cerr << "Error getting terminal attributes." << std::endl; + // exit(1); + // } + + // // Restore original attributes + // term.c_lflag |= (ICANON | ECHO); + + // if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term) == -1) { + // std::cerr << "Error restoring terminal mode." << std::endl; + // exit(1); + // } +#endif + } + namespace cursor { // moves cursor up `n` rows inline void up(unsigned n = 1) { std::cout << ESC << n << "#A"; } @@ -32,8 +123,8 @@ namespace tui { // moves cursor to home position (0;0) inline void home() { std::cout << ESC << 'H'; } - // moves cursor to (`row`;`col`) - inline void move_to(unsigned row, unsigned col) { std::cout << ESC << row << ';' << col << 'H'; } + // moves cursor to (`row`;`col`), both start at 1 + inline void set_position(unsigned row, unsigned col) { std::cout << ESC << row << ';' << col << 'H'; } // moves cursor to column `n` inline void to_column(unsigned n) { std::cout << ESC << n << "#G"; } @@ -45,8 +136,27 @@ namespace tui { // set visibility inline void visible(bool visible) { std::cout << ESC << "?25" << (visible ? 'h' : 'l'); } - // check where the cursor is - inline void request_position() { std::cout << ESC << "6n"; } + // tell the terminal to check where the cursor is + inline void query_position() { std::cout << ESC << "6n"; } + + // (rows;cols) + inline std::pair get_position() { + query_position(); + std::flush(std::cout); + // Read the response: ESC [ rows ; cols R + char ch = 0; + unsigned rows = 0; + unsigned cols = 0; + if (std::cin.get(ch) && ch == PURE_ESC && std::cin.get(ch) && ch == '[') { + std::cin >> rows; + std::cin.get(ch); // skip the ';' + std::cin >> cols; + std::cin.ignore(); // skip the 'R' + } + + return {rows, cols}; + } + } // namespace cursor namespace screen { @@ -67,6 +177,12 @@ namespace tui { inline void scroll_up(unsigned n = 1) { std::cout << ESC << n << 'S'; } inline void scroll_down(unsigned n = 1) { std::cout << ESC << n << 'T'; } + + // get the size of the terminal: (rows;cols) + inline std::pair size() { + cursor::set_position(9999, 9999); // very huge position, that won't be reached, moves to biggest + return cursor::get_position(); + } } // namespace screen namespace text { @@ -106,8 +222,8 @@ namespace tui { #define make_stylizer(STYLE) stylize(STYLE) stylize_text(STYLE) stylize(reset); - inline std::string style_and_reset(const Style& st, const std::string& s) { - return concat(style(st), s, reset_style()); + inline std::string style_and_reset(const Style& st, const std::string& text) { + return concat(style(st), text, reset_style()); } make_stylizer(bold); @@ -118,6 +234,16 @@ namespace tui { make_stylizer(inverted); make_stylizer(invisible); make_stylizer(strikethrough); + + // printf '\e]8;;http://example.com\e\\This is a link\e]8;;\e\\\n' + // printf 'ESC]8;;{link}ESC\\{text}ESC]8;;ESC\\' + inline std::string link(const std::string& link, const std::string& text) { + return concat(PURE_ESC, "]8;;", link, PURE_ESC, "\\", text, PURE_ESC, "]8;;", PURE_ESC, "\\"); + } + inline std::string link(const char* link, const char* text) { + return concat(PURE_ESC, "]8;;", link, PURE_ESC, "\\", text, PURE_ESC, "]8;;", PURE_ESC, "\\"); + } + } // namespace style namespace color { @@ -186,6 +312,7 @@ namespace tui { class tui_string : public std::string { public: tui_string() = default; + template tui_string(T s) : std::string(tui::text::concat(s)) {} tui_string(const char* s) : std::string(s) {} tui_string(const std::string& s) : std::string(s) {} @@ -217,6 +344,7 @@ namespace tui { make_color(white); make_color(basic); + inline tui_string link(const char* link) { return text::style::link(link, *this); } inline tui_string rgb(unsigned r, unsigned g, unsigned b) const { return text::color::rgb(r, g, b, true, *this); } @@ -224,6 +352,37 @@ namespace tui { return text::color::rgb(r, g, b, false, *this); } }; + + // void handle_resize(int /*sig*/) { screen::clear(); } + using fn_ptr = void (*)(int); + // needs a void function, that takes an int, doesn't yet do anything on windows + inline void set_up_resize(fn_ptr handle_resize) { +#ifdef _WIN32 + // should implement logic for windows here +#else + // Register the signal handler for SIGWINCH + struct sigaction sa {}; + sa.sa_handler = handle_resize; + sa.sa_flags = SA_RESTART; // Restart functions if interrupted by handler + sigaction(SIGWINCH, &sa, nullptr); +#endif + } + + inline void init_term(bool enable_cursor) { + tui::enable_raw_mode(); + tui::cursor::visible(enable_cursor); + tui::screen::save_screen(); + tui::screen::alternative_buffer(true); + tui::screen::clear(); + tui::cursor::home(); + } + inline void reset_term() { + tui::disable_raw_mode(); + tui::screen::alternative_buffer(false); + tui::screen::restore_screen(); + tui::cursor::visible(true); + } + } // namespace tui #endif // TUI_H