stamen

Static Menu Generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |

commit199eba24714cb83271e636100315011def07ded9
parent8fe2a8b55ac5c587ad03da22c3e5cc080b7e5687
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateFri, 14 Jun 2024 09:13:49 +0200

General code improvement

Diffstat:
Mdemo/demo_menu.conf|++++++++++++++++++---------------
Minclude/menu.h|+++++++++++++++++++++++++++-------------------------------------
Msrc/menu.cpp|+++++++++++++++++++++++++++++-----------------------
Msrc/stamen.cpp|++++++++------------

4 files changed, 82 insertions(+), 87 deletions(-)


diff --git a/demo/demo_menu.conf b/demo/demo_menu.conf

@@ -1,25 +1,28 @@

+ menu_main Main Menu
+ menu_main Main Menu
- submenu_1 Enter Submenu 1
- submenu_2 Enter Submenu 2
- submenu_1 Enter Submenu 1
- submenu_2 Enter Submenu 2
- finish Quit
+ submenu_1 Submenu 1
- submenu_3 Enter Submenu 3
- operation1 Operation 1
- operation2 Operation 2
- operation3 Operation 3
- submenu_3 Enter Submenu 3
- operation1 Operation 1
- operation2 Operation 2
- operation3 Operation 3
+ submenu_2 Submenu 2
- submenu_3 Enter Submenu 3
- operation1 Operation 1
- operation2 Operation 2
- operation3 Operation 3
- submenu_3 Enter Submenu 3
- operation1 Operation 1
- operation2 Operation 2
- operation3 Operation 3
+ submenu_3 Submenu 3
+ submenu_3 Submenu 3
- operation1 Operation 1
- operation2 Operation 2
- operation3 Operation 3
- operation1 Operation 1
- operation2 Operation 2
- operation3 Operation 3

diff --git a/include/menu.h b/include/menu.h

@@ -21,6 +21,9 @@ class Menu {

Menu(const Menu &) = delete;
Menu &operator=(const Menu &) = delete;
Menu(Menu &&) = delete;
Menu &operator=(Menu &&) = delete;
~Menu() noexcept = default;
static int dynamic(const std::string &code, display_f display) {
Menu::display_stub_default = code;

@@ -29,30 +32,19 @@ class Menu {

};
static void read(const std::string &s);
static void print(const std::string &entry) { print(entry, 1); }
static void insert(const std::string &s, callback_f callback) {
free_lookup.emplace(s, callback);
}
[[nodiscard]] const std::string &getCode() const { return code; }
[[nodiscard]] const std::string &getTitle() const { return title; }
const std::string &getCode() const { return code; }
const std::string &getTitle() const { return title; }
[[nodiscard]] const item_t *getItemv() const {
return entries.items.data();
}
[[nodiscard]] std::size_t getSize() const { return entries.items.size(); }
[[nodiscard]] const std::string &getCode(std::size_t idx) const {
return entries.codes[idx].code;
}
const item_t *getItemv() const { return entries.items.data(); }
std::size_t getSize() const { return entries.items.size(); }
[[nodiscard]] const std::string &getPrompt(std::size_t idx) const {
return entries.codes[idx].prompt;
}
[[nodiscard]] callback_f getCallback(std::size_t idx) const {
return entries.items[idx].callback;
}
callback_f getCallback(std::size_t idx) const;
const std::string &getCode(std::size_t idx) const;
const std::string &getPrompt(std::size_t idx) const;
static std::unordered_map<std::string, Menu> menu_lookup;

@@ -60,36 +52,34 @@ class Menu {

Menu(std::string code, std::string prompt)
: code(std::move(code)), title(std::move(prompt)) {}
static void print(const std::string &entry, const int depth);
static int display_stub(int idx);
struct entries_t {
entries_t() = default;
entries_t(const entries_t &) = delete;
entries_t &operator=(const entries_t &) = delete;
entries_t(entries_t &&) = delete;
entries_t &operator=(entries_t &&) = delete;
~entries_t() noexcept {
for (const auto [_, prompt] : items) {
delete[] prompt;
}
}
void insert(const std::string &code, const std::string &prompt,
callback_f callback = display_stub);
struct Entries {
struct code_t {
std::string code;
std::string prompt;
};
~Entries() {
for (const auto [_, prompt] : items) {
delete[] prompt;
}
}
std::vector<code_t> codes;
std::vector<item_t> items;
void insert(const std::string &code, const std::string &prompt,
callback_f callback = display_stub) {
char *buffer = new char[prompt.size() + 1];
strcpy(buffer, prompt.c_str());
items.emplace_back(callback, buffer);
codes.emplace_back(code, prompt);
}
};
const std::string code, title;
Entries entries;
entries_t entries;
static int display_stub(int idx);
static std::unordered_map<std::string, callback_f> free_lookup;
static std::string display_stub_default;

diff --git a/src/menu.cpp b/src/menu.cpp

@@ -15,48 +15,54 @@ std::unordered_map<std::string, callback_f> Menu::free_lookup;

std::string Menu::display_stub_default;
display_f Menu::display;
void Menu::entries_t::insert(const std::string &code,
const std::string &prompt, callback_f callback) {
char *buffer = new char[prompt.size() + 1];
strcpy(buffer, prompt.c_str());
items.emplace_back(callback, buffer);
codes.emplace_back(code, prompt);
}
const std::string &Menu::getCode(std::size_t idx) const {
return entries.codes[idx].code;
}
const std::string &Menu::getPrompt(std::size_t idx) const {
return entries.codes[idx].prompt;
}
callback_f Menu::getCallback(std::size_t idx) const {
return entries.items[idx].callback;
}
void Menu::read(const std::string &s) {
std::string line, delim, code, prompt;
std::fstream fs(s);
char tmp = 0;
auto last = menu_lookup.end();
while (std::getline(fs, line)) {
if (line.empty()) continue;
std::istringstream ss(line);
ss >> delim >> code;
ss.ignore(1, ' '), std::getline(ss, prompt);
if (delim == "+") {
ss >> delim >> code >> std::ws;
std::getline(ss, prompt);
if (delim != "+") last->second.entries.insert(code, prompt);
else {
const auto [iter, succ] = menu_lookup.emplace(
std::piecewise_construct, std::forward_as_tuple(code),
std::forward_as_tuple(private_ctor_t{}, code, prompt));
last = iter;
} else {
last->second.entries.insert(code, prompt);
}
}
}
void Menu::print(const std::string &code, const int depth) {
const auto it = menu_lookup.find(code);
if (it == menu_lookup.end()) return;
const Menu *menu = &it->second;
if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code);
for (int i = 0; i < menu->getSize(); i++) {
std::cout << std::string(depth << 1, ' ');
std::cout << menu->getPrompt(i);
std::cout << std::format(" ({})\n", menu->getCode(i));
menu->print(code, depth + 1);
}
}
int Menu::display_stub(int idx) {
static std::deque<const Menu *> st;
const std::string &code =
st.size() ? st.back()->getCode(idx) : display_stub_default;
!st.empty() ? st.back()->getCode(idx) : display_stub_default;
const auto ml_it = menu_lookup.find(code);
if (ml_it != menu_lookup.end()) {

@@ -68,7 +74,7 @@ int Menu::display_stub(int idx) {

}
const auto fl_it = free_lookup.find(code);
if (fl_it != free_lookup.end()) { return fl_it->second(0); }
if (fl_it != free_lookup.end()) return fl_it->second(0);
std::cout << "Stamen: nothing to do..." << std::endl;
return 1;

diff --git a/src/stamen.cpp b/src/stamen.cpp

@@ -7,7 +7,6 @@

#include <ostream>
#include <variant>
namespace stamen {
void read(const char *filename) { Menu::read(filename); }

@@ -21,15 +20,14 @@ int dynamic(const char *code, display_f display) {

}
int builtin_display(const char *title, const item_t itemv[], int size) {
const size_t digits = size_t(std::log10(size)) + 1;
const auto items = std::span(itemv, size_t(size));
const size_t dgts = size_t(std::log10(size)) + 1;
int choice = 0;
while (true) {
std::cout << std::format("{}:\n", title);
for (auto i = 0ul; i < size; i++) {
std::cout << std::format(" {:{}}. {}\n", i, digits,
items[i].prompt);
std::cout << std::format(" {:{}}. {}\n", i, dgts, items[i].prompt);
}
while (true) {

@@ -40,20 +38,18 @@ int builtin_display(const char *title, const item_t itemv[], int size) {

return 1;
}
std::cout << std::format("Choice: {}\n\n",
items[choice].prompt);
std::cout << "Choice: " << items[choice].prompt << "\n\n";
const int res = items[choice].callback(choice);
if (res > 1) return res - 1;
else break;
if (res < 2) break;
return res - 1;
} else if (std::cin.eof()) {
std::cerr << "encountered end of input!\n";
return std::numeric_limits<int>::max();
} else {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Invalid option, please choose again!\n";
}
std::cout << std::endl;