ascii box now fixed and back to where it was

v1
Brett 2023-12-15 01:49:42 -05:00
parent 90f271961f
commit 1d52b21131
4 changed files with 168 additions and 83 deletions

View File

@ -313,15 +313,18 @@ namespace blt::string
static inline constexpr size_t MAX_CHILDREN = 16; static inline constexpr size_t MAX_CHILDREN = 16;
struct box_format /**
* Structure which provides variables for the internal spacing of ASCII objects
*/
struct ascii_padding_format
{ {
size_t boxHPadding = 4; size_t horizontalPadding = 4;
size_t boxVPadding = 2; size_t verticalPadding = 2;
}; };
struct tree_format struct tree_format
{ {
box_format boxFormat; ascii_padding_format boxFormat;
int verticalPadding; int verticalPadding;
int horizontalPadding; int horizontalPadding;
@ -429,56 +432,93 @@ namespace blt::string
} }
}; };
class ascii_box class ascii_object
{ {
public: protected:
std::string_view title;
std::string_view data;
const box_format& format;
private:
size_t width_; size_t width_;
size_t height_; size_t height_;
public: public:
ascii_box(std::string_view title, std::string_view data, const box_format& format): title(title), data(data), format(format) ascii_object(size_t width, size_t height): width_(width), height_(height)
{ {}
width_ = std::max(data.length(), title.length()) + (format.boxHPadding * 2);
height_ = 5 + (format.boxVPadding * 2);
}
/**
* @return Internal width of the ascii object. This does not include the bordering box
*/
[[nodiscard]] inline size_t width() const [[nodiscard]] inline size_t width() const
{ {
return width_ + 2; return width_;
} }
/**
* @return Internal height of the ascii object. This does not include the border.
*/
[[nodiscard]] inline size_t height() const [[nodiscard]] inline size_t height() const
{ {
return height_; return height_;
} }
[[nodiscard]] inline size_t raw_width() const /**
* @return full height of the ascii box, includes the expected border around the box
*/
[[nodiscard]] inline size_t full_height() const
{ {
return width_; return height_ + 2;
}
/**
* @return full width of the ascii box, includes the expected border around the box.
*/
[[nodiscard]] inline size_t full_width() const
{
return width_ + 2;
} }
}; };
class ascii_box : public ascii_object
{
public:
std::string_view data;
const ascii_padding_format& format;
public:
explicit ascii_box(std::string_view data,
const ascii_padding_format& format = {}): ascii_object(data.length() + (format.horizontalPadding * 2),
1 + (format.verticalPadding * 2)), data(data), format(format)
{}
};
class ascii_titled_box : public ascii_object
{
public:
std::string_view title;
std::string_view data;
const ascii_padding_format& format;
public:
ascii_titled_box(std::string_view title, std::string_view data,
const ascii_padding_format& format = {}):
ascii_object(std::max(data.length(), title.length()) + (format.horizontalPadding * 2),
3 + (format.verticalPadding * 2)), title(title), data(data), format(format)
{}
};
typedef std::variant<ascii_box, ascii_titled_box> box_type;
class ascii_boxes class ascii_boxes
{ {
private: private:
std::vector<ascii_box> boxes_; std::vector<box_type> boxes_;
size_t width_ = 1; size_t width_ = 1;
size_t height_ = 0; size_t height_ = 0;
public: public:
ascii_boxes() = default; ascii_boxes() = default;
inline void push_back(ascii_box&& box) void push_back(box_type&& box);
inline void push_back(const box_type& box)
{ {
width_ += box.raw_width() + 1; push_back(box_type(box));
// should all be the same
height_ = std::max(box.height(), height_);
boxes_.push_back(box);
} }
inline std::vector<ascii_box>& boxes() inline std::vector<box_type>& boxes()
{ {
return boxes_; return boxes_;
} }
@ -494,9 +534,9 @@ namespace blt::string
} }
}; };
typedef std::variant<ascii_box, ascii_boxes> box_type; typedef std::variant<box_type, ascii_boxes> box_container;
ascii_data constructBox(const box_type& box); ascii_data constructBox(const box_container& box);
class BinaryTreeFormatter class BinaryTreeFormatter
{ {
@ -532,8 +572,8 @@ namespace blt::string
Node* root = nullptr; Node* root = nullptr;
public: public:
explicit BinaryTreeFormatter(std::string rootData, tree_format format = {}): explicit BinaryTreeFormatter(std::string rootData, const tree_format& format = {}):
format(std::move(format)), root(new Node(std::move(rootData))) format(format), root(new Node(std::move(rootData)))
{} {}
std::vector<std::string> generateBox(Node* node) const; std::vector<std::string> generateBox(Node* node) const;

View File

@ -525,19 +525,14 @@ namespace blt
void expand() void expand()
{ {
size_t new_size = m_size * 2; size_t new_size = m_size * 2;
T* data = new T[new_size]; T* data = static_cast<T*>(malloc(sizeof(T) * new_size));
if constexpr (std::is_trivially_copyable_v<T>) if constexpr (std::is_trivially_copyable_v<T>)
std::memcpy(data, m_data, m_size); std::memcpy(data, m_data, m_size);
else if constexpr (std::is_move_assignable_v<T>) else if constexpr (std::is_move_assignable_v<T> || std::is_move_constructible_v<T>)
{ {
for (size_t i = 0; i < m_size; i++) for (size_t i = 0; i < m_size; i++)
data[i] = std::move(m_data[i]); data[i] = std::move(m_data[i]);
} else if constexpr (std::is_move_constructible_v<T>) } else if constexpr (std::is_copy_assignable_v<T> || std::is_copy_constructible_v<T>)
{
// is this bad? probably
for (size_t i = 0; i < m_size; i++)
data[i] = T(std::move(m_data));
} else if constexpr (std::is_copy_assignable_v<T>)
{ {
for (size_t i = 0; i < m_size; i++) for (size_t i = 0; i < m_size; i++)
data[i] = m_data[i]; data[i] = m_data[i];
@ -545,7 +540,7 @@ namespace blt
{ {
static_assert("Unable to use this type with this allocator!"); static_assert("Unable to use this type with this allocator!");
} }
delete[] m_data; free(m_data);
m_data = data; m_data = data;
m_size = new_size; m_size = new_size;
} }

View File

@ -17,6 +17,15 @@ inline constexpr char SEPARATOR = '-';
inline constexpr char CONNECTOR = '+'; inline constexpr char CONNECTOR = '+';
inline constexpr char BAR = '|'; inline constexpr char BAR = '|';
/**
* Returns information from a box_type variant, useful for getting width()/height()
*/
#define getBoxData(variant, getter) \
std::visit(blt::lambda_visitor{ \
[](const blt::string::ascii_box& box) -> auto { return box.getter; }, \
[](const blt::string::ascii_titled_box& box) -> auto { return box.getter; } \
}, variant)
std::vector<std::string> blt::string::TableFormatter::createTable(bool top, bool bottom) std::vector<std::string> blt::string::TableFormatter::createTable(bool top, bool bottom)
{ {
std::vector<std::string> table; std::vector<std::string> table;
@ -224,7 +233,8 @@ std::vector<std::string> blt::string::BinaryTreeFormatter::construct()
size_t lastLineLength = 0; size_t lastLineLength = 0;
const size_t lineHeight = format.verticalPadding * 2 + 3; const size_t lineHeight = format.verticalPadding * 2 + 3;
//std::cout << levels.size() << "\n"; //std::cout << levels.size() << "\n";
const size_t verticalSpacing = format.boxFormat.boxHPadding % 2 == 0 ? format.boxFormat.boxHPadding + 1 : format.boxFormat.boxHPadding; const size_t verticalSpacing =
format.boxFormat.horizontalPadding % 2 == 0 ? format.boxFormat.horizontalPadding + 1 : format.boxFormat.horizontalPadding;
while (!levels.empty()) while (!levels.empty())
{ {
std::vector<std::string> currentLines; std::vector<std::string> currentLines;
@ -247,7 +257,7 @@ std::vector<std::string> blt::string::BinaryTreeFormatter::construct()
BLT_ASSERT(currentLines.size() == box.size() && "Box lines should match current lines!"); BLT_ASSERT(currentLines.size() == box.size() && "Box lines should match current lines!");
for (size_t i = 0; i < currentLines.size(); i++) for (size_t i = 0; i < currentLines.size(); i++)
{ {
currentLines[i] += createPadding(format.boxFormat.boxVPadding); currentLines[i] += createPadding(format.boxFormat.verticalPadding);
//currentLines[i] += createPadding(format.horizontalSpacing + static_cast<std::int64_t>(lineLength / (n.level.size() + 1))); //currentLines[i] += createPadding(format.horizontalSpacing + static_cast<std::int64_t>(lineLength / (n.level.size() + 1)));
currentLines[i] += box[i]; currentLines[i] += box[i];
} }
@ -420,58 +430,86 @@ std::string blt::string::createPadding(size_t length, char spacing)
return padding; return padding;
} }
namespace blt namespace blt::string
{ {
void constructVerticalSeparator(blt::string::ascii_data& data, size_t offset, size_t height) void constructVerticalSeparator(blt::string::ascii_data& data, size_t offset, size_t height)
{ {
for (size_t i = 0; i < height; i++) for (size_t i = 0; i < height; i++)
{ {
if (!(i == 0 || i == height - 1 || i == 2)) if (data.at(offset, i) != '+')
data.at(offset, i) = BAR; data.at(offset, i) = BAR;
} }
} }
void addBox(blt::string::ascii_data& data, const blt::string::ascii_box& box, size_t offset) void addBox(blt::string::ascii_data& data, const blt::string::box_type& box, size_t width_offset, bool has_titled_friends = false,
size_t height_offset = 1)
{ {
// create the horizontal separators size_t vertical_offset = 0;
for (size_t i = 0; i < box.width(); i++) // get box data
auto width = getBoxData(box, width());
auto full_width = getBoxData(box, full_width());
auto height = getBoxData(box, height());
auto full_height = getBoxData(box, full_height());
const auto& box_format = getBoxData(box, format);
const auto& box_data = getBoxData(box, data);
size_t titlePad = box_format.horizontalPadding + 1;
size_t dataPad = box_format.horizontalPadding + 1;
// construct titled box's title
if (has_titled_friends || std::holds_alternative<ascii_titled_box>(box))
{ {
char c = SEPARATOR; for (size_t i = 0; i < full_width; i++)
if (i == 0 || i == box.width() - 1) {
c = CONNECTOR; char c = SEPARATOR;
data.at(offset + i, 0) = c; if (i == 0 || i == full_width - 1)
data.at(offset + i, 2) = c; c = CONNECTOR;
data.at(offset + i, box.height() - 1) = c; data.at(width_offset + i, 2) = c;
}
// TODO: this is ugly
if (std::holds_alternative<ascii_titled_box>(box)){
const auto& box_title = std::get<ascii_titled_box>(box).title;
// 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(width_offset + titlePad + i, 1) = box_title[i];
} else
full_height += 3;
height_offset = 3;
} }
size_t titlePad = box.format.boxHPadding + 1; // create the horizontal separators
size_t dataPad = box.format.boxHPadding + 1; for (size_t i = 0; i < full_width; i++)
{
char c = SEPARATOR;
if (i == 0 || i == full_width - 1)
c = CONNECTOR;
data.at(width_offset + i, 0) = c;
data.at(width_offset + i, full_height - 1) = c;
}
// 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 for (size_t i = 0; i < box_data.size(); i++)
// so we need to add an offset to the smallest string for centering. data.at(width_offset + dataPad + i, height_offset + box_format.verticalPadding) = box_data[i];
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 // add the vertical separator
constructVerticalSeparator(data, offset + box.raw_width() + 1, box.height()); constructVerticalSeparator(data, width_offset + width + 1, full_height);
} }
blt::string::ascii_data blt::string::constructBox(const blt::string::box_type& box) blt::string::ascii_data constructBox(const blt::string::box_container& box)
{ {
auto width = std::visit(blt::lambda_visitor{ auto width = std::visit(blt::lambda_visitor{
[](const blt::string::ascii_box& box) -> size_t { return box.width(); }, [](const blt::string::box_type& box) -> size_t { return getBoxData(box, full_width()); },
[](const blt::string::ascii_boxes& boxes) -> size_t { return boxes.width(); } [](const blt::string::ascii_boxes& boxes) -> size_t { return boxes.width(); }
}, box); }, box);
auto height = std::visit(blt::lambda_visitor{ auto height = std::visit(blt::lambda_visitor{
[](const blt::string::ascii_box& box) -> size_t { return box.height(); }, [](const blt::string::box_type& box) -> size_t { return getBoxData(box, full_height()); },
[](const blt::string::ascii_boxes& boxes) -> size_t { return boxes.height(); } [](const blt::string::ascii_boxes& boxes) -> size_t { return boxes.height(); }
}, box); }, box);
@ -479,22 +517,27 @@ namespace blt
constructVerticalSeparator(data, 0, height); constructVerticalSeparator(data, 0, height);
if (std::holds_alternative<blt::string::ascii_box>(box)) if (std::holds_alternative<blt::string::box_type>(box))
{ addBox(data, std::get<blt::string::box_type>(box), 0);
auto b = std::get<blt::string::ascii_box>(box); else
addBox(data, b, 0);
} else
{ {
auto bv = std::get<blt::string::ascii_boxes>(box); auto bv = std::get<blt::string::ascii_boxes>(box);
size_t offset = 0; size_t offset = 0;
for (const auto& b : bv.boxes()) for (const auto& b : bv.boxes())
{ {
addBox(data, b, offset); addBox(data, b, offset);
offset += b.raw_width() + 1; offset += getBoxData(b, width()) + 1;
} }
} }
return data; return data;
} }
void string::ascii_boxes::push_back(string::box_type&& box)
{
width_ += getBoxData(box, width()) + 1;
// should all be the same
height_ = std::max(getBoxData(box, full_height()), height_);
boxes_.push_back(box);
}
} }

View File

@ -108,7 +108,7 @@ void blt::test::utility::run()
blt::string::tree_format format; blt::string::tree_format format;
format.horizontalPadding = 3; format.horizontalPadding = 3;
format.verticalPadding = 0; format.verticalPadding = 0;
format.boxFormat.boxVPadding = 3; format.boxFormat.verticalPadding = 3;
format.collapse = true; format.collapse = true;
blt::string::BinaryTreeFormatter treeFormatter("I love Men", format); blt::string::BinaryTreeFormatter treeFormatter("I love Men", format);
treeFormatter.getRoot()->with( treeFormatter.getRoot()->with(
@ -148,22 +148,29 @@ void blt::test::utility::run()
testEnumerate(in); testEnumerate(in);
} }
blt::string::box_format bf; blt::string::ascii_padding_format bf;
bf.boxHPadding = 1; bf.horizontalPadding = 1;
bf.boxVPadding = 1; bf.verticalPadding = 1;
blt::string::ascii_box b1{"", "I sold your child", bf}; blt::string::ascii_titled_box bt1{"I have sex", "I sold your child", bf};
blt::string::ascii_box b2{"", "Your my whole world", bf}; blt::string::ascii_titled_box bt2{"With your child", "Your my whole world", bf};
blt::string::ascii_box b1{"Single Love", bf};
blt::string::ascii_box b2{"Never Was Never Will", bf};
auto btd = blt::string::constructBox(bt1);
auto btd1 = blt::string::constructBox(bt2);
auto bd = blt::string::constructBox(b1); auto bd = blt::string::constructBox(b1);
auto bd1 = blt::string::constructBox(b2); auto bd1 = blt::string::constructBox(b2);
blt::string::ascii_boxes boxes; blt::string::ascii_boxes boxes;
boxes.push_back(std::move(b1)); boxes.push_back(std::move(bt1));
boxes.push_back(std::move(b2)); boxes.push_back(std::move(bt2));
auto bd2 = blt::string::constructBox(boxes); auto bd2 = blt::string::constructBox(boxes);
printLines(btd.toVec());
printLines(bd.toVec()); printLines(bd.toVec());
printLines(btd1.toVec());
printLines(bd1.toVec()); printLines(bd1.toVec());
printLines(bd2.toVec()); printLines(bd2.toVec());