/* * menu.c popup menu * * Copyright (C) 2000- Masaki Chikama (Wren) * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "config.h" #ifdef __EMSCRIPTEN__ #include #include #include "portab.h" #include "menu.h" void menu_open(void) { return; } void menu_quitmenu_open(void) { return; } void menu_init(void) { return; } EM_JS(void, menu_setSkipState, (bool enabled, bool activated), { xsystem35.shell.setSkipButtonState(enabled, activated); }); #else // !__EMSCRIPTEN__ #include #include #include #include #include "portab.h" #include "event.h" #include "system.h" #include "nact.h" #include "menu.h" #include "gfx.h" #include "msgskip.h" #include "modal.h" #include "volume.h" // The middle-click popup menu. // What the popup menu resolved to. enum menu_action { MENU_ACTION_NONE, MENU_ACTION_VOLUME, MENU_ACTION_RESET, MENU_ACTION_QUIT, }; struct popup_state { modal base; mu_Rect rect; // popup window rect from the most recent frame enum menu_action action; // what the user selected // A three-finger tap opens the menu, but SDL_GetNumTouchFingers() reports the // live finger count, so it already returns 3 while the first of the three // near-simultaneous FINGERDOWN events is being processed - the menu opens on // that first event. The other two FINGERDOWN events of the same gesture are // still queued and, once the menu is up, would be seen as taps that dismiss // it. Ignore touches until every finger has lifted, then arm tap-to-dismiss. bool touch_armed; }; #ifndef _WIN32 // Windows uses the native menu bar // Message-skip state, kept in sync via menu_setSkipState(). static bool skip_enabled = true; static bool skip_activated; static bool point_in_rect(int x, int y, mu_Rect r) { return x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h; } static bool menu_popup_handler(const SDL_Event *e, modal *modal) { struct popup_state *st = (struct popup_state *)modal; if (e->type == SDL_MOUSEBUTTONUP) { if (e->button.button == SDL_BUTTON_RIGHT || !point_in_rect(e->button.x, e->button.y, st->rect)) st->base.cancelled = true; } else if (e->type == SDL_FINGERUP) { // Arm tap-to-dismiss once the opening gesture's fingers are all lifted. if (SDL_GetNumTouchFingers(e->tfinger.touchId) == 0) st->touch_armed = true; } else if (e->type == SDL_FINGERDOWN && st->touch_armed) { // A tap outside the menu dismisses it. SDL_Point p = event_get_touch_position(&e->tfinger); if (!point_in_rect(p.x, p.y, st->rect)) st->base.cancelled = true; } return modal_default_handler(e, modal); } static bool menu_build(mu_Context *ctx, modal *modal) { struct popup_state *st = (struct popup_state *)modal; bool keep_open = true; int rows = 5; #ifdef __ANDROID__ rows--; #endif int row_h = ctx->style->size.y + ctx->style->padding * 2; int w = 200; // `rows` rows with (rows - 1) inter-row gaps, plus the window's body padding. int h = rows * row_h + (rows - 1) * ctx->style->spacing + ctx->style->padding * 2; mu_Rect r = modal_centered_rect(w, h); st->rect = r; if (mu_begin_window_ex(ctx, "menu", r, MU_OPT_NOTITLE | MU_OPT_NORESIZE | MU_OPT_NOCLOSE | MU_OPT_NOSCROLL)) { mu_layout_row(ctx, 1, (int[]){ -1 }, 0); if (skip_enabled) { int sk = skip_activated; if (mu_checkbox(ctx, _("Message Skip"), &sk)) msgskip_activate(sk); } else { modal_disabled_checkbox(ctx, _("Message Skip"), skip_activated); } #ifndef __ANDROID__ int warp = nact->ags.mouse_warp_enabled; if (mu_checkbox(ctx, _("Mouse Movement"), &warp)) nact->ags.mouse_warp_enabled = warp; #endif if (mu_button(ctx, _("Sound Settings"))) { keep_open = false; st->action = MENU_ACTION_VOLUME; } if (mu_button(ctx, _("Restart"))) { keep_open = false; st->action = MENU_ACTION_RESET; } if (mu_button(ctx, _("Quit"))) { keep_open = false; st->action = MENU_ACTION_QUIT; } mu_end_window(ctx); } if (st->base.cancelled) keep_open = false; return keep_open; } void menu_open(void) { struct popup_state st = { .base = { .build = menu_build, .handler = menu_popup_handler }, .action = MENU_ACTION_NONE, }; modal_run(&st.base); switch (st.action) { case MENU_ACTION_VOLUME: volume_dialog_open(); break; case MENU_ACTION_RESET: menu_resetmenu_open(); break; case MENU_ACTION_QUIT: menu_quitmenu_open(); break; case MENU_ACTION_NONE: break; } } #endif // !_WIN32 static bool confirm_lose_progress(const char *title, const char *confirm_label) { const SDL_MessageBoxButtonData buttons[] = { { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, confirm_label }, { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, _("Cancel") }, }; const SDL_MessageBoxData messagebox_data = { .flags = SDL_MESSAGEBOX_INFORMATION, .window = gfx_getWindow(), .title = title, .message = _("Any unsaved progress will be lost."), .numbuttons = SDL_arraysize(buttons), .buttons = buttons, }; int buttonid = 0; if (SDL_ShowMessageBox(&messagebox_data, &buttonid) < 0) { WARNING("error displaying message box"); return false; } return buttonid == 1; } void menu_quitmenu_open(void) { if (confirm_lose_progress(_("Quit game?"), _("Quit"))) nact_quit(false); } void menu_resetmenu_open(void) { if (confirm_lose_progress(_("Restart game?"), _("Restart"))) nact_quit(true); } // On Windows, menu_open/menu_init/menu_setSkipState are provided by win/menubar.c. #ifndef _WIN32 void menu_init(void) { } void menu_setSkipState(bool enabled, bool activated) { skip_enabled = enabled; skip_activated = activated; } #endif #endif // !__EMSCRIPTEN__