]> git.draconx.ca Git - rrace.git/blob - src/cursmenu.c
Use gen-tree.awk to produce curses menu labels.
[rrace.git] / src / cursmenu.c
1 /*
2  * Curses UI for slide puzzle game
3  * Copyright © 2022 Nick Bowler
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
20 #include <stdlib.h>
21 #include <curses.h>
22
23 #include "cursesui.h"
24 #include "cursmenu.h"
25
26 enum {
27         ACTION_EXIT = 0x80,
28         ACTION_ABOUT,
29         ACTION_NEWGAME,
30         ACTION_MAX
31 };
32
33 static const struct menuitem {
34         uint_least8_t label, position, action;
35 } menu[] = { FUNC_INITIALIZER };
36
37 static void do_function(struct app_state *state, unsigned action)
38 {
39         if (action < sizeof menu / sizeof menu[0]) {
40                 state->toolbar_state = action;
41                 curs_draw_toolbar(state);
42                 doupdate();
43         }
44
45         switch (action) {
46         case ACTION_NEWGAME:
47                 curs_new_game(state);
48                 break;
49         case ACTION_EXIT:
50                 endwin();
51                 exit(0);
52         };
53 }
54
55 void curs_execute_function(struct app_state *state, int func)
56 {
57         int i;
58
59         for (i = state->toolbar_state; menu[i].position; i++) {
60                 if (func == menu[i].position) {
61                         do_function(state, menu[i].action);
62                         return;
63                 }
64         }
65 }
66
67 /*
68  * Return the width of the toolbar and the target width of toolbar labels.
69  */
70 static int toolbar_width(WINDOW *toolbar, int *label_width)
71 {
72         int w, h;
73
74         getmaxyx(toolbar, h, w);
75         *label_width = MAX(6, w/10);
76         return (void)h, w;
77 }
78
79 /*
80  * Given the toolbar function number (between 1 and 10, inclusive), and the
81  * total width of the screen, return the character position for the start of
82  * its label display.
83  *
84  * The intention is to divide the width of the screen into 10 roughly
85  * equally-sized areas, spreading out the remainder so that the width of
86  * each label is a monotone increasing function of the total width.
87  *
88  * The minimum size of a label is 6 characters (2 of which are used for the
89  * number indicator)
90  */
91 static int toolbar_xpos(int i, int total_width, int label_width)
92 {
93         int rem = total_width - 10*label_width;
94         int pos = (i-1)*label_width;
95
96         switch (rem) {
97         case 9: pos += i > 6;
98         case 8: pos += i > 2;
99         case 7: pos += i > 7;
100         case 6: pos += i > 3;
101         case 5: pos += i > 8;
102         case 4: pos += i > 4;
103         case 3: pos += i > 9;
104         case 2: pos += i > 5;
105         }
106
107         return pos;
108 }
109
110 /*
111  * Redraw the current set of toolbar labels.
112  */
113 void curs_draw_toolbar(struct app_state *state)
114 {
115         WINDOW *toolbar = state->toolbar;
116         int i, w, lw;
117
118         werase(toolbar);
119
120         w = toolbar_width(toolbar, &lw);
121         for (i = state->toolbar_state; menu[i].position; i++) {
122                 int pos = toolbar_xpos(menu[i].position, w, lw);
123                 mvwprintw(toolbar, 0, pos+2, "%.*s", lw, strtab+menu[i].label);
124         }
125
126         mvwchgat(toolbar, 0, 0, -1, A_REVERSE, RR_COLOUR_TOOLBAR, 0);
127         for (i = 1; i <= 10; i++) {
128                 mvwprintw(toolbar, 0, toolbar_xpos(i, w, lw), "%2d", i);
129         }
130
131         wnoutrefresh(state->toolbar);
132 }
133
134 #if HAVE_CURSES_MOUSE_SUPPORT
135 /*
136  * Returns the toolbar function (1 through 10) under the given x, y screen
137  * coordinates, or 0 if the coordinates are outside of the toolbar.
138  */
139 int curs_toolbar_mouse_func(struct app_state *state, int x, int y)
140 {
141         int toolbar_x, toolbar_y, i, w, lw;
142
143         getbegyx(state->toolbar, toolbar_y, toolbar_x);
144         if ((void)toolbar_x, y != toolbar_y)
145                 return 0;
146
147         /* OK, selected a button, determine which one */
148         w = toolbar_width(state->toolbar, &lw);
149         for (i = 10; i > 1; i--) {
150                 if (x >= toolbar_xpos(i, w, lw))
151                         break;
152         }
153
154         return i;
155 }
156 #endif