stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | 199eba24714cb83271e636100315011def07ded9 | 
| parent | 8fe2a8b55ac5c587ad03da22c3e5cc080b7e5687 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Fri, 14 Jun 2024 09:13:49 +0200 | 
General code improvement
| M | demo/demo_menu.conf | | | ++++++++++++++++++ --------------- | 
| M | include/menu.h | | | +++++++++++++++++++++++++++ ------------------------------------- | 
| M | src/menu.cpp | | | +++++++++++++++++++++++++++++ ----------------------- | 
| M | src/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;