diff --git a/include/blt/std/format.h b/include/blt/std/format.h index 5fc4667..fcb11db 100755 --- a/include/blt/std/format.h +++ b/include/blt/std/format.h @@ -377,12 +377,12 @@ namespace blt::string return *this; }; - char at(size_t x, size_t y) + inline char& at(size_t x, size_t y) { - return data_[x * width_ + y]; + return data_[x * height_ + y]; } - char* data() + inline char* data() { return data_; } @@ -394,7 +394,7 @@ namespace blt::string { std::string line; line.reserve(width()); - for (int i = 0; i < width(); i++) + for (size_t i = 0; i < width(); i++) { line += at(i, j); } @@ -431,13 +431,15 @@ namespace blt::string class ascii_box { - private: + public: std::string_view title; std::string_view data; + const box_format& format; + private: size_t width_; size_t height_; public: - ascii_box(std::string_view title, std::string_view data, const box_format& format): title(title), data(data) + ascii_box(std::string_view title, std::string_view data, const box_format& format): title(title), data(data), format(format) { width_ = std::max(data.length(), title.length()) + (format.boxHPadding * 2); height_ = 5 + (format.boxVPadding * 2); @@ -463,16 +465,16 @@ namespace blt::string { private: std::vector boxes_; - size_t width = 1; - size_t height = 0; + size_t width_ = 1; + size_t height_ = 0; public: ascii_boxes() = default; inline void push_back(ascii_box&& box) { - width += box.raw_width() + 1; + width_ += box.raw_width() + 1; // should all be the same - height = std::max(box.height(), height); + height_ = std::max(box.height(), height_); boxes_.push_back(box); } @@ -480,6 +482,16 @@ namespace blt::string { return boxes_; } + + [[nodiscard]] inline size_t width() const + { + return width_; + } + + [[nodiscard]] inline size_t height() const + { + return height_; + } }; typedef std::variant box_type; diff --git a/src/blt/std/format.cpp b/src/blt/std/format.cpp index 79967cf..baf1b34 100755 --- a/src/blt/std/format.cpp +++ b/src/blt/std/format.cpp @@ -12,6 +12,10 @@ #include #include +inline constexpr char SEPARATOR = '-'; +inline constexpr char CONNECTOR = '+'; +inline constexpr char BAR = '|'; + std::vector blt::string::TableFormatter::createTable(bool top, bool bottom) { std::vector table; @@ -219,7 +223,7 @@ std::vector blt::string::BinaryTreeFormatter::construct() size_t lastLineLength = 0; const size_t lineHeight = format.verticalPadding * 2 + 3; //std::cout << levels.size() << "\n"; - const size_t verticalSpacing = format.boxHPadding % 2 == 0 ? format.boxHPadding + 1 : format.boxHPadding; + const size_t verticalSpacing = format.boxFormat.boxHPadding % 2 == 0 ? format.boxFormat.boxHPadding + 1 : format.boxFormat.boxHPadding; while (!levels.empty()) { std::vector currentLines; @@ -242,7 +246,7 @@ std::vector blt::string::BinaryTreeFormatter::construct() BLT_ASSERT(currentLines.size() == box.size() && "Box lines should match current lines!"); for (size_t i = 0; i < currentLines.size(); i++) { - currentLines[i] += createPadding(format.boxVPadding); + currentLines[i] += createPadding(format.boxFormat.boxVPadding); //currentLines[i] += createPadding(format.horizontalSpacing + static_cast(lineLength / (n.level.size() + 1))); currentLines[i] += box[i]; } @@ -415,8 +419,101 @@ std::string blt::string::createPadding(size_t length, char spacing) return padding; } -blt::string::ascii_data blt::string::constructBox(const blt::string::box_type& box) +namespace blt { - return blt::string::ascii_data(0, 0); -} + struct getHeight + { + inline size_t operator()(const blt::string::ascii_box& box) + { + return box.height(); + } + + inline size_t operator()(const blt::string::ascii_boxes& boxes) + { + return boxes.height(); + } + }; + + struct getWidth + { + inline size_t operator()(const blt::string::ascii_box& box) + { + return box.width(); + } + + inline size_t operator()(const blt::string::ascii_boxes& boxes) + { + return boxes.width(); + } + }; + + void constructVerticalSeparator(blt::string::ascii_data& data, size_t offset, size_t height) + { + for (size_t i = 0; i < height; i++) + { + if (!(i == 0 || i == height - 1 || i == 2)) + data.at(offset, i) = BAR; + } + } + + void addBox(blt::string::ascii_data& data, const blt::string::ascii_box& box, size_t offset) + { + // create the horizontal separators + for (size_t i = 0; i < box.width(); i++) + { + char c = SEPARATOR; + if (i == 0 || i == box.width() - 1) + c = CONNECTOR; + data.at(offset + i, 0) = c; + data.at(offset + i, 2) = c; + data.at(offset + i, box.height() - 1) = c; + } + + size_t titlePad = box.format.boxHPadding + 1; + size_t dataPad = box.format.boxHPadding + 1; + + // if one of the strings are larger than there will be a misalignment as the width of the box is based on the largest string + // so we need to add an offset to the smallest string for centering. + if (box.data.length() > box.title.length()) + titlePad += (box.data.length() - box.title.length())/2; + else + dataPad += (box.title.length() - box.data.length())/2; + + // copy in the title and data string + for (size_t i = 0; i < box.title.size(); i++) + data.at(offset + titlePad + i, 1) = box.title[i]; + for (size_t i = 0; i < box.data.size(); i++) + data.at(offset + dataPad + i, 3 + box.format.boxVPadding) = box.data[i]; + // add the vertical separator + constructVerticalSeparator(data, offset + box.raw_width() + 1, box.height()); + } + + blt::string::ascii_data blt::string::constructBox(const blt::string::box_type& box) + { + auto width = std::visit(getWidth(), box); + auto height = std::visit(getHeight(), box); + + string::ascii_data data(width, height); + + constructVerticalSeparator(data, 0, height); + + if (std::holds_alternative(box)) + { + auto b = std::get(box); + addBox(data, b, 0); + } else + { + auto bv = std::get(box); + size_t offset = 0; + for (const auto& b : bv.boxes()) + { + addBox(data, b, offset); + offset += b.raw_width() + 1; + } + } + + return data; + } + +} \ No newline at end of file diff --git a/tests/src/utility_test.cpp b/tests/src/utility_test.cpp index b46dcab..93702b0 100644 --- a/tests/src/utility_test.cpp +++ b/tests/src/utility_test.cpp @@ -105,10 +105,10 @@ void blt::test::utility::run() printLines(tableTest.createTable(true, true)); - blt::string::BinaryTreeFormatter::TreeFormat format; + blt::string::tree_format format; format.horizontalPadding = 3; format.verticalPadding = 0; - format.boxVPadding = 3; + format.boxFormat.boxVPadding = 3; format.collapse = true; blt::string::BinaryTreeFormatter treeFormatter("I love Men", format); treeFormatter.getRoot()->with( @@ -148,6 +148,25 @@ void blt::test::utility::run() testEnumerate(in); } + blt::string::box_format bf; + bf.boxHPadding = 1; + bf.boxVPadding = 1; + blt::string::ascii_box b1{"", "I sold your child", bf}; + blt::string::ascii_box b2{"", "Your my whole world", bf}; + + auto bd = blt::string::constructBox(b1); + auto bd1 = blt::string::constructBox(b2); + + blt::string::ascii_boxes boxes; + boxes.push_back(std::move(b1)); + boxes.push_back(std::move(b2)); + + auto bd2 = blt::string::constructBox(boxes); + + printLines(bd.toVec()); + printLines(bd1.toVec()); + printLines(bd2.toVec()); + for (auto r : blt::range(0, 10)) BLT_TRACE_STREAM << r << " "; BLT_TRACE_STREAM << "\n";