/* * 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 . */ #include #include #include #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 *item_width) { int w, h; getmaxyx(toolbar, h, w); *item_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 item_width) { int rem = total_width - 10*item_width; int pos = (i-1)*item_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