stamen

Stamen - static menu generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE

commit b833ffadb09c2e07d744ac2e30ecb0562d8b89be
parent 1d14de3dcbe573ba1a156facdc0d477558f3c97e
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Thu,  9 Nov 2023 18:53:33 +0000

Refactoring

* Back to static menu interface
* Greatly simplified the implementation without loss of functionality

Diffstat:
MCMakeLists.txt | 2+-
Minclude/menu.h | 86++++++++++++++++++++++++++-----------------------------------------------------
Msrc/CMakeLists.txt | 2+-
Dsrc/display.cpp | 60------------------------------------------------------------
Msrc/main.cpp | 16++++++++--------
Msrc/menu.cpp | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 97 insertions(+), 128 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Menu - VERSION 0.0.2 + VERSION 0.0.3 DESCRIPTION "Experimentation with dinamic menus" LANGUAGES CXX ) diff --git a/include/menu.h b/include/menu.h @@ -10,46 +10,16 @@ using json = nlohmann::json; -class EMenu_callback : std::exception { - virtual const char *what() const noexcept override { - return "Unknown callback function or menu method"; - } -}; - -class EMenu_call : std::exception { - virtual const char *what() const noexcept override { - return "Trying a callback that doesn't exist"; - } -}; - class Menu { public: typedef int (*callback_f)(void); - Menu(const std::string &s) { - for (const auto &menu : json::parse(std::fstream(s))) { - const json &items = menu["items"]; - const function_t *mf = - new function_t(*this, menu["name"], {items.begin(), items.end()}); - lookup.insert({menu["code"], callback_t(mf)}); + class EMenu : std::exception { + virtual const char *what() const noexcept override { + return "Trying to access an unknown menu"; } - } - - void print(const std::string code = "main", const int depth = 1) const; - void operator()() const { get_callback("main")(); } - - struct record_t { - const std::string code; - const callback_f callback; }; - void insert(const record_t &record) { - lookup.insert({record.code, record.callback}); - } - void insert(const std::vector<record_t> &records) { - for (const auto &record : records) { insert(record); } - } - private: struct item_t { const std::string prompt; @@ -60,40 +30,40 @@ private: item_t(const json &j) : item_t(j["prompt"], j["callback"]) {} }; - struct function_t { - friend class Menu; - function_t(const Menu &m, const std::string &n, - const std::vector<item_t> &i) - : menu(m), name(n), items(i) {} + Menu(const json &json_data) + : name(json_data["name"]), code(json_data["code"]), + items({json_data["items"].begin(), json_data["items"].end()}) {} - int display() const; + Menu(const std::string code, const callback_f callback) + : name(code), code(code), items(), callback(callback) {} - private: - const Menu &menu; - const std::string name; - const std::vector<item_t> items; - }; +public: + static void read(const std::string &s) { + for (const auto &json_data : json::parse(std::fstream(s))) { + lookup.insert({json_data["code"], Menu(json_data)}); + } + } - struct callback_t { - const function_t *menu_func = nullptr; - const callback_f func = nullptr; + static int start() { return getMenu("main")(); } + static void insert(const std::string code, const callback_f callback) { + lookup.insert({code, Menu(code, callback)}); + } - callback_t(const callback_f f) : func(f) {} - callback_t(const function_t *f) : menu_func(f) {} + static void print(const std::string code = "main", const int depth = 1); + int operator()() const; - int operator()() const { - if (!func && !menu_func) throw EMenu_callback(); - return func ? func() : menu_func->display(); - } - }; +private: + static std::unordered_map<std::string, Menu> lookup; - const callback_t &get_callback(const std::string &s) const { - const auto it = lookup.find(s); - if (it == lookup.end()) throw EMenu_call(); + static const Menu &getMenu(const std::string &code) { + const auto it = lookup.find(code); + if (it == lookup.end()) throw EMenu(); return it->second; } - std::unordered_map<std::string, callback_t> lookup; + const std::string name, code; + const std::vector<item_t> items; + const callback_f callback = nullptr; }; #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(menu - display.cpp + menu.cpp ) target_include_directories(menu diff --git a/src/display.cpp b/src/display.cpp @@ -1,60 +0,0 @@ -#include "menu.h" -#include <cmath> -#include <format> -#include <iostream> - -int Menu::function_t::display() const { - int choice; - - const int n = items.size(), digits = std::log10(n) + 1; - while (true) { - std::cout << std::format("{}:\n", name); - for (auto i = 0ul; i < n; i++) { - std::cout << std::format(" {:{}}. {}\n", i, digits, items[i].prompt); - } - - while (true) { - std::cout << "Choose an option: "; - if (std::cin >> choice && choice >= -1 && choice < n) { - if (choice == -1) { - std::cout << "choice: back\n"; - return 1; - } - - std::cout << std::format("Choice: {}\n\n", items[choice].prompt); - int res = menu.get_callback(items[choice].callback)(); - if (--res) return res; - - break; - } 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::cout << "Invalid option, please choose again!\n"; - } - std::cout << std::endl; - } - - return 1; -} - -void Menu::print(const std::string code, const int depth) const { - const auto it = lookup.find(code); - if (it == lookup.end()) return; - const callback_t &cb = it->second; - - if (depth == 1) { - std::cout << std::format("{}({})\n", cb.menu_func->name, code); - } - - if (cb.menu_func) { - for (const auto &item : cb.menu_func->items) { - std::cout << std::format("{}{}({})\n", std::string(depth << 1, ' '), - item.prompt, item.callback); - if (cb.menu_func) cb.menu_func->menu.print(item.callback, depth + 1); - } - } -} diff --git a/src/main.cpp b/src/main.cpp @@ -19,13 +19,13 @@ int finish(void) { } int main(void) { - Menu menu("menu.json"); - menu.insert({ - {"algorithms", algorithms}, - { "settings", settings}, - { "finish", finish} - }); - // menu(); - menu.print(); + Menu::read("menu.json"); + Menu::insert("algorithms", algorithms); + Menu::insert("settings", settings); + Menu::insert("finish", finish); + + Menu::print("main"); + Menu::start(); + return 0; } diff --git a/src/menu.cpp b/src/menu.cpp @@ -1,2 +1,61 @@ #include "menu.h" +#include <cmath> +#include <format> +#include <iostream> +std::unordered_map<std::string, Menu> Menu::lookup; + +int Menu::operator()() const { + if (callback) return callback(); + + int choice; + const int n = items.size(), digits = std::log10(n) + 1; + while (true) { + std::cout << std::format("{}:\n", name); + for (auto i = 0ul; i < n; i++) { + std::cout << std::format(" {:{}}. {}\n", i, digits, items[i].prompt); + } + + while (true) { + std::cout << "Choose an option: "; + if (std::cin >> choice && choice >= -1 && choice < n) { + if (choice == -1) { + std::cout << "choice: back\n"; + return 1; + } + + std::cout << std::format("Choice: {}\n\n", items[choice].prompt); + int res = getMenu(items[choice].callback)(); + if (--res) return res; + + break; + } 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::cout << "Invalid option, please choose again!\n"; + } + std::cout << std::endl; + } + + return 1; +} + +void Menu::print(const std::string code, const int depth) { + const auto it = lookup.find(code); + if (it == lookup.end()) return; + const Menu &menu = it->second; + + if (depth == 1) { std::cout << std::format("{}({})\n", menu.name, code); } + + if (!menu.callback) { + for (const auto &item : menu.items) { + std::cout << std::format("{}{}({})\n", std::string(depth << 1, ' '), + item.prompt, item.callback); + menu.print(item.callback, depth + 1); + } + } +}