#ifndef OB_RECT_HH #define OB_RECT_HH #include "ob/color.hh" #include "ob/text.hh" #include "ob/term.hh" namespace aec = OB::Term::ANSI_Escape_Codes; #include #include #include #include #include #include namespace OB { class Rect { public: enum class Align { center, top, bottom, left, right }; Rect() = default; Rect(Rect&&) = default; Rect(Rect const&) = default; ~Rect() = default; Rect& operator=(Rect&&) = default; Rect& operator=(Rect const&) = default; friend std::ostream& operator<<(std::ostream& os, Rect& obj) { obj.draw(os); return os; } Rect& draw(std::ostream& os) { bool _border {false}; if (_border_top || _border_bottom || _border_left || _border_right) { _border = true; } bool _padding {false}; if (_padding_top || _padding_bottom || _padding_left || _padding_right) { _padding = true; } auto padding_top = _padding_top; auto padding_bottom = _padding_bottom; auto padding_left = _padding_left; auto padding_right = _padding_right; if (_border && _padding) { if (_border_top) { ++padding_top; } if (_border_bottom) { ++padding_bottom; } if (_border_left) { ++padding_left; } if (_border_right) { ++padding_right; } } std::size_t text_width {_w - _border_left - _border_right - _padding_left - _padding_right}; std::size_t text_height {_h - _border_top - _border_bottom - _padding_top - _padding_bottom}; std::size_t row {0}; auto const ln = OB::String::split(_text, "\n"); OB::Text::View lnv; os << aec::clear << _color_fg << _color_bg; bool style_main {true}; std::size_t y_begin {0}; // determine y axis alignment switch (_y_align) { case Align::center: { if (text_height / 2 >= ln.size() / 2) { y_begin = text_height / 2 - ln.size() / 2; } break; } case Align::bottom: { if (text_height >= ln.size()) { y_begin = text_height - ln.size(); } break; } case Align::top: default: { break; } } for (std::size_t y = 0; y < _h && y + _y <= _y_max; ++y) { os << aec::cursor_set(_x, y + _y); std::size_t cursor_right {0}; for (std::size_t x = 0; x < _w && x + _x <= _x_max; ++x) { if (_border) { if (y == 0 || x == 0) { if (y == 0 && x == 0 && (_border_top || _border_left)) { if (style_main) { os << _border_fg << _border_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << _corner_top_left; cursor_right = 0; continue; } else if (y == 0 && x + 1 == _w && (_border_top || _border_right)) { if (style_main) { os << _border_fg << _border_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << _corner_top_right; cursor_right = 0; continue; } else if (y + 1 == _h && x == 0 && (_border_bottom || _border_left)) { if (style_main) { os << _border_fg << _border_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << _corner_bottom_left; cursor_right = 0; continue; } else if (y == 0 && _border_top) { if (style_main) { os << _border_fg << _border_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << _line_top; cursor_right = 0; continue; } else if (x == 0 && _border_left) { if (style_main) { os << _border_fg << _border_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << _line_left; cursor_right = 0; continue; } } else if (y + 1 == _h && x + 1 == _w && (_border_bottom || _border_right)) { if (style_main) { os << _border_fg << _border_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << _corner_bottom_right; cursor_right = 0; continue; } else if (y + 1 == _h && _border_bottom) { if (style_main) { os << _border_fg << _border_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << _line_bottom; cursor_right = 0; continue; } else if (x + 1 == _w && _border_right) { if (style_main) { os << _border_fg << _border_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << _line_right; cursor_right = 0; continue; } } if (! style_main) { os << _color_fg.step() << _color_bg; style_main = ! style_main; } if (_padding) { if (y < padding_top) { ++cursor_right; continue; } if (y + padding_bottom >= _h) { ++cursor_right; continue; } if (x < padding_left) { ++cursor_right; continue; } if (x + padding_right >= _w) { ++cursor_right; continue; } } if (! _text.empty() && y >= y_begin && row < ln.size()) { // set the view to the current line lnv.str(ln.at(row++)); // line syntax highlighting syntax(lnv); // total columns in the current line auto tcols = lnv.cols(); // total columns used so far std::size_t cols {0}; int x_begin {0}; // determine x axis alignment switch (_x_align) { case Align::center: { if (text_width / 2 >= tcols / 2) { x_begin = text_width / 2 - tcols / 2; } break; } case Align::right: { if (text_width >= tcols) { x_begin = text_width - tcols; } break; } case Align::left: default: { break; } } while (x_begin-- > 0) { ++cursor_right; ++cols; } os << aec::cursor_right(cursor_right); cursor_right = 0; { lnv.str(lnv.colstr(0, tcols <= text_width ? text_width : text_width - 1)); cols += lnv.cols(); std::size_t pos_line {0}; std::size_t pos_syntax {0}; for (auto const& e : lnv) { // TODO does not handle when syntax pos has multiple equal values if (pos_syntax < _syntax.size() && _syntax.at(pos_syntax).first == pos_line) { os << _highlight.at(_syntax.at(pos_syntax).second).second << e << aec::clear; ++pos_syntax; style_main = false; } else { if (_color_fg.mode() == OB::Color::Mode::party) { _color_fg.step(); style_main = ! style_main; } if (! style_main) { os << _color_fg << _color_bg; style_main = ! style_main; } os << e; } ++pos_line; } if (tcols <= text_width) { while (cols < text_width) { ++cursor_right; ++cols; } } else { while (cols + 1 < text_width) { ++cursor_right; ++cols; } if (! style_main) { os << _color_fg << _color_bg; style_main = ! style_main; } os << aec::cursor_right(cursor_right) << ">"; cursor_right = 0; ++cols; } } // subtract 1 to counter the for loops ++x condition x += cols - 1; } else { if (_fill == " " && _color_bg.key() == "clear") { ++cursor_right; } else { os << aec::cursor_right(cursor_right) << _color_fg.step() << _fill; cursor_right = 0; } } } } return *this; } Rect& highlight(std::vector> const& hl) { _highlight = hl; return *this; } Rect& align(Align x, Align y) { _x_align = x; _y_align = y; return *this; } Rect& xy(std::size_t x, std::size_t y) { _x = ++x; _y = ++y; return *this; } Rect& xy_max(std::size_t x, std::size_t y) { _x_max = x; _y_max = y; return *this; } Rect& wh(std::size_t w, std::size_t h) { _w = w; _h = h; return *this; } Rect& fill(std::string const& str) { _fill = str; return *this; } Rect& color_fg(OB::Color const& color) { _color_fg = color; return *this; } Rect& color_bg(OB::Color const& color) { _color_bg = color; return *this; } Rect& text(std::string const& val) { _text = val; return *this; } Rect& border(bool top, bool right, bool bottom, bool left) { _border_top = top; _border_bottom = bottom; _border_left = left; _border_right = right; return *this; } Rect& border_fg(OB::Color const& color) { _border_fg = color; return *this; } Rect& border_bg(OB::Color const& color) { _border_bg = color; return *this; } Rect& padding(std::size_t top, std::size_t right, std::size_t bottom, std::size_t left) { _padding_top = top; _padding_bottom = bottom; _padding_left = left; _padding_right = right; return *this; } private: void syntax(OB::Text::View const& view) { _syntax.clear(); std::size_t pos {0}; OB::Text::Regex search; for (auto const& [regex, color] : _highlight) { search.match(regex, view.str()); if (! search.empty()) { for (auto const& match : search) { _syntax.emplace_back(view.byte_to_char(match.pos), pos); } } ++pos; } std::sort(_syntax.begin(), _syntax.end(), [](auto const& lhs, auto const& rhs) { return lhs.first < rhs.first; }); } std::size_t _x {0}; std::size_t _y {0}; std::size_t _x_max {0}; std::size_t _y_max {0}; Align _x_align {Align::left}; Align _y_align {Align::top}; std::size_t _w {0}; std::size_t _h {0}; std::string _fill {" "}; OB::Color _color_fg {OB::Color::Type::fg}; OB::Color _color_bg {OB::Color::Type::bg}; // text OB::Text::String _text; std::vector> _syntax; std::vector> _highlight; // border bool _border_top {false}; bool _border_bottom {false}; bool _border_left {false}; bool _border_right {false}; OB::Color _border_fg {OB::Color::Type::fg}; OB::Color _border_bg {OB::Color::Type::bg}; std::string _line_top {"─"}; std::string _line_bottom {"─"}; std::string _line_left {"│"}; std::string _line_right {"│"}; std::string _corner_top_left {"┌"}; std::string _corner_top_right {"┐"}; std::string _corner_bottom_left {"└"}; std::string _corner_bottom_right {"┘"}; // padding std::size_t _padding_top {0}; std::size_t _padding_bottom {0}; std::size_t _padding_left {0}; std::size_t _padding_right {0}; }; // class Rect } // namespace OB #endif // OB_RECT_HH