]> git.draconx.ca Git - rrace.git/blobdiff - src/cursmenu.c
Use gen-tree.awk to produce curses menu labels.
[rrace.git] / src / cursmenu.c
diff --git a/src/cursmenu.c b/src/cursmenu.c
new file mode 100644 (file)
index 0000000..e665a32
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Curses UI for slide puzzle game
+ * Copyright © 2022 Nick Bowler
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <curses.h>
+
+#include "cursesui.h"
+#include "cursmenu.h"
+
+enum {
+       ACTION_EXIT = 0x80,
+       ACTION_ABOUT,
+       ACTION_NEWGAME,
+       ACTION_MAX
+};
+
+static const struct menuitem {
+       uint_least8_t label, position, action;
+} menu[] = { FUNC_INITIALIZER };
+
+static void do_function(struct app_state *state, unsigned action)
+{
+       if (action < sizeof menu / sizeof menu[0]) {
+               state->toolbar_state = action;
+               curs_draw_toolbar(state);
+               doupdate();
+       }
+
+       switch (action) {
+       case ACTION_NEWGAME:
+               curs_new_game(state);
+               break;
+       case ACTION_EXIT:
+               endwin();
+               exit(0);
+       };
+}
+
+void curs_execute_function(struct app_state *state, int func)
+{
+       int i;
+
+       for (i = state->toolbar_state; menu[i].position; i++) {
+               if (func == menu[i].position) {
+                       do_function(state, menu[i].action);
+                       return;
+               }
+       }
+}
+
+/*
+ * Return the width of the toolbar and the target width of toolbar labels.
+ */
+static int toolbar_width(WINDOW *toolbar, int *label_width)
+{
+       int w, h;
+
+       getmaxyx(toolbar, h, w);
+       *label_width = MAX(6, w/10);
+       return (void)h, w;
+}
+
+/*
+ * Given the toolbar function number (between 1 and 10, inclusive), and the
+ * total width of the screen, return the character position for the start of
+ * its label display.
+ *
+ * The intention is to divide the width of the screen into 10 roughly
+ * equally-sized areas, spreading out the remainder so that the width of
+ * each label is a monotone increasing function of the total width.
+ *
+ * The minimum size of a label is 6 characters (2 of which are used for the
+ * number indicator)
+ */
+static int toolbar_xpos(int i, int total_width, int label_width)
+{
+       int rem = total_width - 10*label_width;
+       int pos = (i-1)*label_width;
+
+       switch (rem) {
+       case 9: pos += i > 6;
+       case 8: pos += i > 2;
+       case 7: pos += i > 7;
+       case 6: pos += i > 3;
+       case 5: pos += i > 8;
+       case 4: pos += i > 4;
+       case 3: pos += i > 9;
+       case 2: pos += i > 5;
+       }
+
+       return pos;
+}
+
+/*
+ * Redraw the current set of toolbar labels.
+ */
+void curs_draw_toolbar(struct app_state *state)
+{
+       WINDOW *toolbar = state->toolbar;
+       int i, w, lw;
+
+       werase(toolbar);
+
+       w = toolbar_width(toolbar, &lw);
+       for (i = state->toolbar_state; menu[i].position; i++) {
+               int pos = toolbar_xpos(menu[i].position, w, lw);
+               mvwprintw(toolbar, 0, pos+2, "%.*s", lw, strtab+menu[i].label);
+       }
+
+       mvwchgat(toolbar, 0, 0, -1, A_REVERSE, RR_COLOUR_TOOLBAR, 0);
+       for (i = 1; i <= 10; i++) {
+               mvwprintw(toolbar, 0, toolbar_xpos(i, w, lw), "%2d", i);
+       }
+
+       wnoutrefresh(state->toolbar);
+}
+
+#if HAVE_CURSES_MOUSE_SUPPORT
+/*
+ * Returns the toolbar function (1 through 10) under the given x, y screen
+ * coordinates, or 0 if the coordinates are outside of the toolbar.
+ */
+int curs_toolbar_mouse_func(struct app_state *state, int x, int y)
+{
+       int toolbar_x, toolbar_y, i, w, lw;
+
+       getbegyx(state->toolbar, toolbar_y, toolbar_x);
+       if ((void)toolbar_x, y != toolbar_y)
+               return 0;
+
+       /* OK, selected a button, determine which one */
+       w = toolbar_width(state->toolbar, &lw);
+       for (i = 10; i > 1; i--) {
+               if (x >= toolbar_xpos(i, w, lw))
+                       break;
+       }
+
+       return i;
+}
+#endif