#include "Menus.h" #include #include #include #include #include #include "Colors.h" #include "Random.h" #include "Utils.h" namespace SBF { namespace { using std::cout; using std::endl; using std::ostream; using std::pair; using std::to_string; using std::vector; } // End namespace void BuildMenuWithValues(vector items, vector labels, vector values); void BuildMenuWithColors(vector items, vector labels, vector colors); void AdjustMenuStyle(MenuStyle& style, vector items, bool ignoreValue); string GetTitle(MenuItem item, MenuStyle style); string GetTitleWithoutValue(MenuItem item, MenuStyle style); void NewMenuStyle(MenuStyle& style); void NewMenuItem(MenuItem& item, string label, int id); void NewMenuItemWithValue(MenuItem& item, string label, int id, int value); void NewMenuItemWithColor(MenuItem& item, string label, int id, uint8_t color); void MenuStyle::Adjust(vector menu_items, bool ignore_value) { size_t max_id_width = 0; size_t max_item_width = 0; size_t max_value_width = 0; for_each(menu_items.begin(), menu_items.end(), [&](MenuItem menu_item) { if (menu_item.is_visible) { max_id_width = std::max(max_id_width, to_string(menu_item.id).size()); max_item_width = std::max(max_item_width, (menu_item.label + label_value_separator).size()); max_value_width = std::max(max_value_width, to_string(menu_item.value).size()); } }); if (show_random) { max_id_width = std::max(max_id_width, to_string(random_item_id).size()); max_item_width = std::max(max_item_width, random_item_name.size()); } if (show_cancel) { max_id_width = std::max(max_id_width, to_string(cancel_item_id).size()); max_item_width = std::max(max_item_width, cancel_item_name.size()); } id_width = max_id_width; label_width = max_item_width; value_width = ignore_value ? 0 : max_value_width; } ostream& PrintMenu(ostream& os, vector items, MenuStyle style) { MenuItem random_item = MenuItem(style.random_item_name, style.random_item_id, style.random_item_color); MenuItem cancel_item = MenuItem(style.cancel_item_name, style.cancel_item_id, style.cancel_item_color); size_t actual_count = 0; for_each(items.begin(), items.end(), [&actual_count](MenuItem item) { if (item.is_visible) { actual_count++; } }); if (style.show_cancel) { actual_count++; } if (style.show_random) { actual_count++; } if (actual_count <= 10) { for_each(items.begin(), items.end(), [&style, &os](MenuItem item) { if (item.is_visible) { string title = GetTitle(item, style); PrintWithMaybeColor(os, title, item.color, style.use_colors) << endl; } }); if (style.show_cancel) { string title = GetTitleWithoutValue(cancel_item, style); PrintWithMaybeColor(os, title, cancel_item.color, style.use_colors) << endl; } if (style.show_random) { string title = GetTitleWithoutValue(random_item, style); PrintWithMaybeColor(os, title, random_item.color, style.use_colors) << endl; } } else { MenuItem empty_item; size_t item_width = GetTitle(empty_item, style).size(); size_t items_per_row = style.screen_width / (item_width + style.menu_item_spacer.size()); size_t column_width = style.screen_width / items_per_row; const size_t count = items.size(); int column = 0; for (int i = 0; i < count; i++) { const MenuItem& item = items[i]; if (item.is_visible) { string item_text = GetTitle(item, style); if (column != items_per_row - 1) { if (i != count || style.show_random || style.show_cancel) { size_t text_length = item_text.size(); item_text = MakeFitL( RightTrim(item_text) + style.menu_item_spacer, text_length + style.menu_item_spacer.size(), ' '); } } string label = MakeFitC(item_text, column_width, ' '); PrintWithMaybeColor(os, label, item.color, style.use_colors); column = (column + 1) % items_per_row; if (column == 0) { cout << endl; } } } if (style.show_cancel) { string title = MakeFitC(GetTitleWithoutValue(cancel_item, style), column_width, ' '); PrintWithMaybeColor(os, title, cancel_item.color, style.use_colors) << endl; } if (style.show_random) { string title = MakeFitC(GetTitleWithoutValue(random_item, style), column_width, ' '); PrintWithMaybeColor(os, title, random_item.color, style.use_colors) << endl; } } return os; } vector BuildMenu(vector labels) { vector menu_items; int id = 1; for_each( labels.begin(), labels.end(), [&menu_items, &id](string label) { menu_items.push_back(MenuItem(label, id++)); }); return menu_items; } vector BuildMenuWithValues(vector> items) { vector menu_items; size_t id = 1; for (auto pr : items) { menu_items.push_back(MenuItem(pr.first, id, pr.second)); } return menu_items; } vector BuildMenuWithValues(vector labels, vector values) { vector menu_items; size_t count = std::min(labels.size(), values.size()); for (size_t i = 0; i < count; i++) { menu_items.push_back(MenuItem(labels.at(i), i + 1, values.at(i))); } return menu_items; } vector BuildMenuWithColors(vector> items) { vector menu_items; size_t id = 1; for (auto pr : items) { menu_items.push_back(MenuItem(pr.first, id, pr.second)); } return menu_items; } vector BuildMenuWithColors(vector labels, vector colors) { vector menu_items; size_t count = std::min(labels.size(), colors.size()); for (size_t i = 0; i < count; i++) { menu_items.push_back(MenuItem(labels.at(i), i + 1, colors.at(i))); } return menu_items; } int GetRandomMenuItemId(vector items) { int num_visible_items = 0; size_t count = items.size(); vector visible_item_ids; for (int i = 0; i < count; i++) { MenuItem item = items.at(i); if (item.is_visible && item.include_in_random) { visible_item_ids.push_back(item.id); num_visible_items++; } } return visible_item_ids[GetRandomInt(0, num_visible_items - 1)]; } MenuStyle::MenuStyle() : id_width(0), label_width(0), value_width(0), screen_width(80), random_item_name("Random"), random_item_id(0), random_item_color(kColorDefaultForeground), cancel_item_name("Cancel"), cancel_item_id(-1), cancel_item_color(kColorDefaultForeground), id_label_separator(" = "), label_value_separator(": "), menu_item_spacer(", "), show_random(true), show_cancel(false), use_colors(false) {} MenuItem::MenuItem() : label(""), id(0), value(0), color(kColorDefaultForeground), is_visible(true), include_in_random(true) {} MenuItem::MenuItem(string label, int id) : label(label), id(id), value(0), color(kColorDefaultForeground), is_visible(true), include_in_random(true) {} MenuItem::MenuItem(string label, int id, int value) : label(label), id(id), value(value), color(kColorDefaultForeground), is_visible(true), include_in_random(true) {} MenuItem::MenuItem(string label, int id, uint8_t color) : label(label), id(id), value(0), color(color), is_visible(true), include_in_random(true) {} ostream& PrintWithMaybeColor(ostream& os, const string& text, uint8_t text_color, bool use_colors) { uint8_t previous_color = GetForegroundColor(); if (use_colors) { SetForegroundColor(text_color); os << ForegroundColor; } os << text; if (use_colors) { SetForegroundColor(previous_color); os << ForegroundColor; } return os; } string GetTitleWithoutValue(MenuItem item, MenuStyle style) { string id_string = MakeFitR(to_string(item.id), style.id_width, ' '); int label_width = style.label_width + style.value_width + style.label_value_separator.size(); string label_string = MakeFitL(item.label, label_width, ' '); return id_string + style.id_label_separator + label_string; } string GetTitle(MenuItem item, MenuStyle style) { string id = to_string(item.id); string label = item.label; if (style.value_width > 0) { label += style.label_value_separator; } string value = to_string(item.value); string formatted_id = MakeFitR(id, style.id_width); string formatted_label = MakeFitL(label, style.label_width); string formatted_value = MakeFitR(value, style.value_width); return formatted_id + style.id_label_separator + formatted_label + formatted_value; } std::ostream& operator<<(std::ostream& os, const MenuStyle& style) { os << "MenuStyle {id_width: " << style.id_width << ", label_width: " << style.label_width << ", value_width: " << style.value_width << ", screen_width: " << style.screen_width << ", show_random: " << (style.show_random ? "true" : "false") << ", random_item_id: " << style.random_item_id << ", random_item_name: " << style.random_item_name << ", random_item_color: " << (int)style.random_item_color << ", show_cancel: " << (style.show_cancel ? "true" : "false") << ", cancel_item_id: " << style.cancel_item_id << ", cancel_item_name: " << style.cancel_item_name << ", cancel_item_color: " << (int)style.cancel_item_color << ", id_label_separator: \"" << style.id_label_separator << "\", label_value_separator: \"" << style.label_value_separator << "\", menu_item_spacer: \"" << style.menu_item_spacer << "\", use_colors: " << (style.use_colors ? "true" : "false") << "}"; return os; } std::ostream& operator<<(std::ostream& os, const MenuItem& item) { os << "MenuItem {id: " << item.id << ", label: \"" << item.label << "\", value: " << item.value << ", color: " << (int)item.color << ", is_visible: " << (item.is_visible ? "true" : "false") << ", include_in_random: " << (item.include_in_random ? "true" : "false") << "}"; return os; } bool MenuStyle::operator==(const MenuStyle& other) { return cancel_item_color == other.cancel_item_color && cancel_item_id == other.cancel_item_id && cancel_item_name == other.cancel_item_name && id_label_separator == other.id_label_separator && id_width == other.id_width && label_value_separator == other.label_value_separator && label_width == other.label_width && menu_item_spacer == other.menu_item_spacer && random_item_color == other.random_item_color && random_item_id == other.random_item_id && random_item_name == other.random_item_name && screen_width == other.screen_width && show_cancel == other.show_cancel && show_random == other.show_random && use_colors == other.use_colors && value_width == other.value_width; } bool MenuStyle::operator!=(const MenuStyle& other) { return !(*this == other); } bool MenuItem::operator==(const MenuItem& other) { return color == other.color && id == other.id && include_in_random == other.include_in_random && is_visible == other.is_visible && label == other.label && value == other.value; } bool MenuItem::operator!=(const MenuItem& other) { return !(*this == other); } } // End namespace SBF