#include "menu.nvgt" #include "audio_paths.nvgt" // Applies common menu sounds from a directory. // Looks for both .ogg and .wav files automatically. void menu_apply_default_sounds(menu @menuRef, const string soundDir = "sounds/menu") { if (@menuRef is null) { return; } menuRef.click_sound = resolve_audio_path(soundDir + "/menu_move"); menuRef.select_sound = resolve_audio_path(soundDir + "/menu_select"); menuRef.edge_sound = resolve_audio_path(soundDir + "/menu_edge"); menuRef.wrap_sound = resolve_audio_path(soundDir + "/menu_wrap"); menuRef.open_sound = resolve_audio_path(soundDir + "/menu_open"); menuRef.close_sound = resolve_audio_path(soundDir + "/menu_close"); } // Minimal blocking list menu. // Returns selected index or -1 on empty input/escape. int menu_run_simple(const string introText, string[] @options, bool wrap = true, int startIndex = 0, const string soundDir = "sounds/menu") { if (@options is null || options.length() == 0) { return -1; } menu menuRef; menu_apply_default_sounds(menuRef, soundDir); menuRef.intro_text = introText; menuRef.wrap = wrap; menuRef.focus_first_item = true; menuRef.add_items(options); if (startIndex >= 0 && startIndex < int(options.length())) { menuRef.focused_item = startIndex; } return menuRef.run(); } // Returns a-z for menu prefix filtering, or empty string when no letter key was pressed. string menu_get_filter_letter() { if (key_pressed(KEY_A)) return "a"; if (key_pressed(KEY_B)) return "b"; if (key_pressed(KEY_C)) return "c"; if (key_pressed(KEY_D)) return "d"; if (key_pressed(KEY_E)) return "e"; if (key_pressed(KEY_F)) return "f"; if (key_pressed(KEY_G)) return "g"; if (key_pressed(KEY_H)) return "h"; if (key_pressed(KEY_I)) return "i"; if (key_pressed(KEY_J)) return "j"; if (key_pressed(KEY_K)) return "k"; if (key_pressed(KEY_L)) return "l"; if (key_pressed(KEY_M)) return "m"; if (key_pressed(KEY_N)) return "n"; if (key_pressed(KEY_O)) return "o"; if (key_pressed(KEY_P)) return "p"; if (key_pressed(KEY_Q)) return "q"; if (key_pressed(KEY_R)) return "r"; if (key_pressed(KEY_S)) return "s"; if (key_pressed(KEY_T)) return "t"; if (key_pressed(KEY_U)) return "u"; if (key_pressed(KEY_V)) return "v"; if (key_pressed(KEY_W)) return "w"; if (key_pressed(KEY_X)) return "x"; if (key_pressed(KEY_Y)) return "y"; if (key_pressed(KEY_Z)) return "z"; return ""; } // Applies a prefix filter to menu options. void menu_apply_prefix_filter(const string& in filterText, const string[] @options, int[] @filteredIndices, string[] @filteredOptions) { filteredIndices.resize(0); filteredOptions.resize(0); if (@options is null) { return; } string filterLower = filterText.lower(); for (uint optionIndex = 0; optionIndex < options.length(); optionIndex++) { if (filterLower.length() == 0) { filteredIndices.insert_last(optionIndex); filteredOptions.insert_last(options[optionIndex]); continue; } string optionLower = options[optionIndex].lower(); if (optionLower.length() >= filterLower.length() && optionLower.substr(0, filterLower.length()) == filterLower) { filteredIndices.insert_last(optionIndex); filteredOptions.insert_last(options[optionIndex]); } } } // Updates filter text from keyboard input and reapplies filtering. bool menu_update_prefix_filter(string& inout filterText, const string[] @options, int[] @filteredIndices, string[] @filteredOptions, int& inout selection) { bool filterChanged = false; if (key_pressed(KEY_BACK) && filterText.length() > 0) { filterText = filterText.substr(0, filterText.length() - 1); filterChanged = true; } string filterLetter = menu_get_filter_letter(); if (filterLetter != "") { filterText += filterLetter; filterChanged = true; } if (filterChanged) { menu_apply_prefix_filter(filterText, options, filteredIndices, filteredOptions); if (selection < 0) { selection = 0; } if (selection >= int(filteredOptions.length())) { selection = 0; } } return filterChanged; }