Pārlūkot izejas kodu

Working MenuDispatch.

If any of the menu options are > 1 char, then you have
to type it in, and hit enter.

Escape always aborts the menu entry -- input set to empty.
Steve Thielemann 3 gadi atpakaļ
vecāks
revīzija
5cde698208
2 mainītis faili ar 284 papildinājumiem un 4 dzēšanām
  1. 263 3
      dispatchers.cpp
  2. 21 1
      dispatchers.h

+ 263 - 3
dispatchers.cpp

@@ -22,7 +22,7 @@ void Dispatch::notify(void) {
   }
   }
 }
 }
 
 
-MainDispatch::MainDispatch(Session *s) : Dispatch{s}, id{s} {
+MainDispatch::MainDispatch(Session *s) : Dispatch{s}, id{s}, md{s} {
   BUGZ_LOG(warning) << "MainDispatch()";
   BUGZ_LOG(warning) << "MainDispatch()";
 }
 }
 
 
@@ -58,7 +58,7 @@ void MainDispatch::activate(void) {
 
 
   // to_client("\n\r\x1b[1;34mWELCOME!  You are now in the proxy zone...\n\r");
   // to_client("\n\r\x1b[1;34mWELCOME!  You are now in the proxy zone...\n\r");
   id.prompt = "\x1b[0m    \x1b[1;33;44m-=>\x1b[0m \x1b[1;37;44m";
   id.prompt = "\x1b[0m    \x1b[1;33;44m-=>\x1b[0m \x1b[1;37;44m";
-  id.max_length = 5;
+  id.max_length = 15;
   id.setNotify([this]() { this->have_input(); });
   id.setNotify([this]() { this->have_input(); });
   id.activate();
   id.activate();
 }
 }
@@ -72,8 +72,39 @@ void MainDispatch::have_input(void) {
   if (id.input == "?") {
   if (id.input == "?") {
     // Maybe?  Maybe not.
     // Maybe?  Maybe not.
   }
   }
+
+  if (id.input == "menu") {
+    md.menu_box_color = "\x1b[1;33;44m";
+    md.menu_text_color = "\x1b[1;37;44m";
+    md.menu_title = "Proxy Menu";
+    md.menu_options_color = "\x1b[1;36;40m";
+    // md.menu_prompt = " --==>> ";
+
+    // bold white to white ---  black to green
+    // md.menu_prompt = "\x1b[1;37;47m\xdb\xb2\xb1\xb0\x1b[0;30;47m RED GREEN
+    // \x1b[30;42m\xdb\xb2\xb1\xb0\x1b[0m : ";
+
+    // md.menu_prompt = "\x1b[0;31;47m\xdb\xb2\xb1\xb0\x1b[0;30;47m RED
+    // GREEN\x1b[37;42m\xdb\xb2\xb1\xb0\x1b[0m : ";
+    const char *CP437_GRADIENT = "\xdb\xb2\xb1\xb0 "; // 100, 75, 50, 25, 0
+
+    md.menu_prompt = "\x1b[0;31;40m\xdb\xb2\xb1\xb0 \x1b[31;40mRED "
+                     "\x1b[32;40mGREEN\x1b[30;42m\xdb\xb2\xb1\xb0 \x1b[0m : ";
+    md.lazy = true;
+    md.menu = {{"A", "Apple"}, {"B", "Blue"}, {"R", "Rabbit"}, {"Z", "ZOOO!"}};
+    md.setNotify([this]() { this->menu_choice(); });
+    md.activate();
+    return;
+  }
+
+  if (id.input == "menu2") {
+    md.lazy = false;
+    md.setNotify([this]() { this->menu_choice(); });
+    md.activate();
+    return;
+  }
   if (id.input.empty()) {
   if (id.input.empty()) {
-  // if (count >= 5) {
+    // if (count >= 5) {
     auto lines = Boxes::alert(" Returning you to the game... ", "",
     auto lines = Boxes::alert(" Returning you to the game... ", "",
                               "\x1b[1;32m", 30, 1, true);
                               "\x1b[1;32m", 30, 1, true);
     // I'm not setting the box color, so the last color bleeds over.
     // I'm not setting the box color, so the last color bleeds over.
@@ -90,6 +121,21 @@ void MainDispatch::have_input(void) {
   }
   }
 }
 }
 
 
+void MainDispatch::menu_choice(void) {
+  if (md.input.empty()) {
+    to_client("Menu abort.\n\r");
+  } else {
+    std::string text = "Back from menu [";
+    text.append(md.input);
+    text.append("] was your selection.\n\r");
+    to_client(text);
+  }
+
+  id.max_length = 15;
+  id.setNotify([this]() { this->have_input(); });
+  id.activate();
+}
+
 void MainDispatch::deactivate(void) {
 void MainDispatch::deactivate(void) {
   // Since we're the main thing there --
   // Since we're the main thing there --
   sess->emit_server_line = nullptr;
   sess->emit_server_line = nullptr;
@@ -137,6 +183,8 @@ InputDispatch::InputDispatch(Session *s) : Dispatch(s) {
   BUGZ_LOG(warning) << "InputDispatch()";
   BUGZ_LOG(warning) << "InputDispatch()";
 }
 }
 
 
+InputDispatch::~InputDispatch() { BUGZ_LOG(warning) << "~InputDispatch()"; }
+
 void InputDispatch::activate(void) {
 void InputDispatch::activate(void) {
   ds = sess->save_settings();
   ds = sess->save_settings();
   sess->emit_server_line = [this](const std::string &s) { server_line(s); };
   sess->emit_server_line = [this](const std::string &s) { server_line(s); };
@@ -181,6 +229,218 @@ void InputDispatch::client_input(const std::string &cinput) {
   }
   }
 }
 }
 
 
+/**
+ * Menu Dispatch
+ *
+ * Two types of menus:
+ * lazy:  display the menu name and show prompt. ? shows menu.
+ * non-lazy:  displays menu + prompts.
+ *
+ *
+ */
+
+MenuDispatch::MenuDispatch(Session *s) : Dispatch{s} {
+  BUGZ_LOG(warning) << "MenuDispatch()";
+}
+
+MenuDispatch::~MenuDispatch() { BUGZ_LOG(warning) << "~MenuDispatch()"; }
+
+void MenuDispatch::activate(void) {
+  calculate_widths();
+  input.clear();
+
+  BUGZ_LOG(warning) << "MenuDispatch::activate() " << max_width << ", "
+                    << max_option_width;
+
+  ds = sess->save_settings();
+  sess->emit_server_line = [this](const std::string &s) { server_line(s); };
+  sess->emit_server_prompt = nullptr;
+  sess->emit_client_input = [this](const std::string &s) { client_input(s); };
+
+  if (lazy)
+    menubox();
+  else
+    help();
+  to_client(menu_prompt);
+}
+
+void MenuDispatch::deactivate(void) {
+  sess->restore_settings(ds);
+  notify();
+}
+
+void MenuDispatch::help(void) {
+  size_t max = max_width;
+  Boxes mbox(max, 1, true);
+  mbox.boxcolor = menu_box_color;
+
+  if (lazy) {
+    // just the menu
+    mbox.textcolor = menu_options_color;
+    to_client(mbox.top());
+    for (auto const menu_item : menu) {
+      std::string text = " ";
+      text.append(menu_item.first);
+      text.append(" - ");
+      text.append(menu_item.second);
+      while (text.length() < max)
+        text.append(1, ' ');
+      to_client(mbox.row(text));
+    }
+    to_client(mbox.bottom());
+  } else {
+    // full menu
+    mbox.textcolor = menu_text_color;
+    to_client(mbox.top());
+    std::string title = centered(max, menu_title);
+    BUGZ_LOG(debug) << "help max=" << max << " [" << title << "]";
+
+    to_client(mbox.row(title));
+    to_client(mbox.middle());
+    mbox.textcolor = menu_options_color;
+    for (auto const menu_item : menu) {
+      std::string text = " ";
+      text.append(menu_item.first);
+      text.append(" - ");
+      text.append(menu_item.second);
+      while (text.length() < max)
+        text.append(1, ' ');
+      to_client(mbox.row(text));
+    }
+    to_client(mbox.bottom());
+  }
+}
+
+std::string MenuDispatch::centered(int length, const std::string &s) {
+  std::string text = s;
+  size_t leftovers = length - text.length();
+  int count = leftovers / 2;
+  if (count > 0) {
+    text.insert(0, count, ' ');
+    text.append(count, ' ');
+  }
+
+  if (leftovers % 1 == 1)
+    text.append(1, ' ');
+  return text;
+}
+
+void MenuDispatch::menubox(void) {
+  // just the menu box
+  std::string title = centered(max_width, menu_title);
+  /*
+  int leftovers = max - menu_title.length();
+  while (leftovers > 2) {
+    title.insert(0, 1, " ");
+    title.append(1, " ");
+    leftovers -= 2;
+  };
+  if (leftovers == 1)
+    title.append(1, " ");
+  */
+  BUGZ_LOG(debug) << "menubox max=" << max_width << " [" << title << "]";
+
+  auto abox =
+      Boxes::alert(title, menu_box_color, menu_text_color, max_width, 1, true);
+  for (auto line : abox) {
+    to_client(line);
+  }
+}
+
+void MenuDispatch::calculate_widths(void) {
+  max_width = menu_title.length() + 2;
+  max_option_width = 0;
+  for (auto key : menu) {
+    size_t menu_line_length =
+        1 + key.first.length() + 3 + key.second.length() + 1;
+    if (menu_line_length > max_width)
+      max_width = menu_line_length;
+    if (key.first.length() > max_option_width)
+      max_option_width = key.first.length();
+  }
+  instant = max_option_width == 1;
+}
+
+void MenuDispatch::server_line(const std::string &line) {}
+
+void MenuDispatch::client_input(const std::string &cinput) {
+  for (auto const ch : cinput) {
+    // not likely that we'd have more then one,
+    // but deal with it correctly.
+
+    if (ch == '\r') {
+      // enter
+      if (instant)
+        return;
+
+      for (auto const mnu : menu) {
+        if (mnu.first == input) {
+          to_client("\x1b[0m\n\r");
+          deactivate();
+          return;
+        }
+      }
+
+      // input wasn't found ?
+      while (input.length() > 0) {
+        to_client("\b \b");
+        input.erase(input.length() - 1);
+      }
+      return; // don't continue ...
+    }
+
+    if (ch == '\x1b') {
+      // [ESC] - erase the input string
+      while (input.length() > 0) {
+        to_client("\b \b");
+        input.erase(input.length() - 1);
+      }
+
+      // Exit - allow escape from menu
+      deactivate();
+      return;
+    }
+
+    if (ch == '\b') {
+      if (input.length() > 0) {
+        to_client("\b \b");
+        input.erase(input.length() - 1);
+      }
+    }
+
+    if (ch == '?') {
+      to_client(cinput); // display what they entered.
+      to_client("\x1b[0m\n\r");
+      help();
+      to_client(menu_prompt);
+      return;
+    }
+
+    if (isprint(ch)) {
+      char c = ch;
+      if (!case_sensitive)
+        c = toupper(ch);
+
+      // ok, it's a printable character
+      if (input.length() < max_option_width) {
+        // ok, there's room.
+        to_client(std::string(1, c));
+        input.append(1, c);
+      }
+
+      if (instant) {
+        for (auto const mnu : menu) {
+          if (mnu.first == input) {
+            to_client("\x1b[0m\n\r");
+            deactivate();
+            return;
+          }
+        }
+      }
+    }
+  }
+}
+
 /*
 /*
  * CoreDispatch:  This is an example class that does dispatch.
  * CoreDispatch:  This is an example class that does dispatch.
  * Copy this and make changes from there...
  * Copy this and make changes from there...

+ 21 - 1
dispatchers.h

@@ -53,6 +53,7 @@ private:
 
 
 public:
 public:
   InputDispatch(Session *);
   InputDispatch(Session *);
+  ~InputDispatch();
 
 
   std::string prompt;
   std::string prompt;
   size_t max_length;
   size_t max_length;
@@ -70,10 +71,27 @@ public:
 class MenuDispatch : public Dispatch {
 class MenuDispatch : public Dispatch {
 private:
 private:
   DispatchSettings ds;
   DispatchSettings ds;
+  void help(void);
+  void menubox(void);
+  size_t max_width;
+  size_t max_option_width;
+  bool instant = false;
+  void calculate_widths(void);
+  std::string centered(int length, const std::string &);
 
 
 public:
 public:
   MenuDispatch(Session *);
   MenuDispatch(Session *);
-
+  ~MenuDispatch();
+
+  std::string menu_box_color;
+  std::string menu_text_color;
+  std::string menu_title;
+  std::string menu_options_color;
+  std::string menu_prompt;
+  bool lazy = true;
+  std::map<std::string, std::string> menu;
+  bool case_sensitive = false;
+  std::string input;
   int choice;
   int choice;
 
 
   void activate(void) override;
   void activate(void) override;
@@ -97,6 +115,7 @@ public:
 class MainDispatch : public Dispatch {
 class MainDispatch : public Dispatch {
 private:
 private:
   InputDispatch id;
   InputDispatch id;
+  MenuDispatch md;
 
 
 public:
 public:
   MainDispatch(Session *);
   MainDispatch(Session *);
@@ -106,6 +125,7 @@ public:
   void deactivate(void) override;
   void deactivate(void) override;
 
 
   void have_input(void);
   void have_input(void);
+  void menu_choice(void);
 
 
   void server_line(const std::string &line);
   void server_line(const std::string &line);
   void server_prompt(const std::string &prompt);
   void server_prompt(const std::string &prompt);