stamen

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

commit260569531d18b4c35adeaa7b53c866ebea1e2d28
parent0ab8d3b1b0cae32d598a861f4fa04f95b348d54c
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateTue, 7 Nov 2023 01:37:21 +0000

Refactoring * Better initializastion of function lookup * Consturctor now reads configuration directly * More consistent menu display * Remove menu.cpp * Renaming * Start menu with operator()

Diffstat:
MCMakeLists.txt|+-
Minclude/menu.h|+++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/CMakeLists.txt|+-----
Msrc/display.cpp|+++++++++++++++---------
Msrc/main.cpp|++++++++----
Msrc/menu.cpp|---------

6 files changed, 66 insertions(+), 59 deletions(-)


diff --git a/CMakeLists.txt b/CMakeLists.txt

@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(
Menu
VERSION 0.0.1
VERSION 0.0.2
DESCRIPTION "Experimentation with dinamic menus"
LANGUAGES CXX
)

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

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

#include <vector>
using json = nlohmann::json;
using std::string, std::vector;
class EMenu_callback : std::exception {
virtual const char *what() const noexcept override {

@@ -25,49 +24,60 @@ class EMenu_call : std::exception {

class Menu {
public:
typedef int (*Menu_f)(void);
Menu() : function_lookup() {}
Menu(const string &s) : Menu() {
std::fstream f(s);
Read(f);
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)});
}
}
void Start() const { get_callback("main")(); }
void operator()() const { get_callback("main")(); }
void Read(std::istream &is);
Menu &Register(const string &s, Menu_f f) {
function_lookup.insert({s, f});
return *this;
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 Menu_item {
const string prompt;
const string callback;
struct item_t {
const std::string prompt;
const std::string callback;
Menu_item(const string &p, const string &c) : prompt(p), callback(c) {}
Menu_item(const json &j) : Menu_item(j["prompt"], j["callback"]) {}
item_t(const std::string &p, const std::string &c)
: prompt(p), callback(c) {}
item_t(const json &j) : item_t(j["prompt"], j["callback"]) {}
};
struct Menu_function {
Menu_function(const Menu &m, const string &n, const vector<Menu_item> &i)
struct function_t {
function_t(const Menu &m, const std::string &n,
const std::vector<item_t> &i)
: menu(m), name(n), items(i) {}
int display() const;
private:
const Menu &menu;
const string name;
const vector<Menu_item> items;
const std::string name;
const std::vector<item_t> items;
};
struct Menu_callback {
const Menu_f func = nullptr;
const Menu_function *menu_func = nullptr;
struct callback_t {
const function_t *menu_func = nullptr;
const callback_f func = nullptr;
Menu_callback(const Menu_f f) : func(f) {}
Menu_callback(const Menu_function *f) : menu_func(f) {}
callback_t(const callback_f f) : func(f) {}
callback_t(const function_t *f) : menu_func(f) {}
int operator()() const {
if (!func && !menu_func) throw EMenu_callback();

@@ -75,13 +85,13 @@ private:

}
};
const Menu_callback &get_callback(const string &s) const {
auto it = function_lookup.find(s);
if (it == function_lookup.end()) throw EMenu_call();
return (*it).second;
const callback_t &get_callback(const std::string &s) const {
const auto it = lookup.find(s);
if (it == lookup.end()) throw EMenu_call();
return it->second;
}
std::unordered_map<string, Menu_callback> function_lookup;
std::unordered_map<std::string, callback_t> lookup;
};
#endif

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt

@@ -1,5 +1,4 @@

add_library(menu
menu.cpp
display.cpp
)

@@ -7,10 +6,7 @@ target_include_directories(menu

PUBLIC ../include
)
add_executable(demo
main.cpp
)
add_executable(demo main.cpp)
target_link_libraries(demo
PRIVATE menu

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

@@ -1,24 +1,30 @@

#include "menu.h"
#include <cmath>
#include <format>
#include <iostream>
int Menu::Menu_function::display() const {
int Menu::function_t::display() const {
int choice;
while (true) {
std::cout << name << std::endl;
for (auto i = 0ul; i < items.size(); i++)
std::cout << i << ". " << items[i].prompt << std::endl;
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 < (int)items.size()) {
if (std::cin >> choice && choice >= -1 && choice < n) {
if (choice == -1) {
std::cout << "Back" << items[choice].prompt << "\n";
std::cout << "choice: back\n";
return 1;
}
std::cout << "Chosen: " << items[choice].prompt << "\n\n";
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";

@@ -27,7 +33,7 @@ int Menu::Menu_function::display() const {

std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
std::cout << "Invalid option, please choose again!" << std::endl;
std::cout << "Invalid option, please choose again!\n";
}
std::cout << std::endl;
}

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

@@ -3,11 +3,13 @@

int algorithms(void) {
std::cout << "algorithms" << std::endl;
std::cout << "nothing to do here" << std::endl;
return 1;
}
int settings(void) {
std::cout << "settings" << std::endl;
std::cout << "nothing to do here" << std::endl;
return 1;
}

@@ -18,9 +20,11 @@ int finish(void) {

int main(void) {
Menu menu("menu.json");
menu.Register("algorithms", algorithms)
.Register("settings", settings)
.Register("finish", finish);
menu.Start();
menu.insert({
{"algorithms", algorithms},
{ "settings", settings},
{ "finish", finish}
});
menu();
return 0;
}

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

@@ -1,11 +1,2 @@

#include "menu.h"
void Menu::Read(std::istream &is) {
json file = json::parse(is);
for (auto &menu : file) {
const json &items = menu["items"];
const Menu_function *mf =
new Menu_function(*this, menu["name"], {items.begin(), items.end()});
function_lookup.insert({menu["code"], Menu_callback(mf)});
}
}