| 1 | #include <algorithm> |
| 2 | #include <dirent.h> |
| 3 | #include <unistd.h> |
| 4 | #include "cartridge.hpp" |
| 5 | #include "menu.hpp" |
| 6 | |
| 7 | namespace GUI { |
| 8 | |
| 9 | using namespace std; |
| 10 | |
| 11 | |
| 12 | Entry::Entry(string label, function<void()> callback) : callback(callback) |
| 13 | { |
| 14 | set_label(label); |
| 15 | } |
| 16 | |
| 17 | Entry::~Entry() |
| 18 | { |
| 19 | SDL_DestroyTexture(whiteTexture); |
| 20 | SDL_DestroyTexture(redTexture); |
| 21 | } |
| 22 | |
| 23 | void Entry::set_label(string label) |
| 24 | { |
| 25 | this->label = label; |
| 26 | |
| 27 | if (whiteTexture != nullptr) SDL_DestroyTexture(whiteTexture); |
| 28 | if (redTexture != nullptr) SDL_DestroyTexture(redTexture); |
| 29 | |
| 30 | whiteTexture = gen_text(label, { 255, 255, 255 }); |
| 31 | redTexture = gen_text(label, { 255, 0, 0 }); |
| 32 | } |
| 33 | |
| 34 | void Entry::render(int x, int y) { |
| 35 | render_texture(selected ? redTexture : whiteTexture, x, y); |
| 36 | } |
| 37 | |
| 38 | ControlEntry::ControlEntry(string action, SDL_Scancode* key) : key(key), |
| 39 | Entry::Entry( |
| 40 | action, |
| 41 | [&]{ keyEntry->set_label(SDL_GetScancodeName(*(this->key) = query_key())); }) |
| 42 | { |
| 43 | this->keyEntry = new Entry(SDL_GetScancodeName(*key), []{}); |
| 44 | } |
| 45 | |
| 46 | ControlEntry::ControlEntry(string action, int* button) : button(button), |
| 47 | Entry::Entry( |
| 48 | action, |
| 49 | [&]{ keyEntry->set_label(to_string(*(this->button) = query_button())); }) |
| 50 | { |
| 51 | this->keyEntry = new Entry(to_string(*button), []{}); |
| 52 | } |
| 53 | |
| 54 | |
| 55 | void Menu::(Entry* entry) |
| 56 | { |
| 57 | if (entries.empty()) |
| 58 | entry->select(); |
| 59 | entries.push_back(entry); |
| 60 | } |
| 61 | |
| 62 | void Menu::() |
| 63 | { |
| 64 | for (auto entry : entries) |
| 65 | delete entry; |
| 66 | entries.clear(); |
| 67 | clear_error(); |
| 68 | cursor = 0; |
| 69 | } |
| 70 | |
| 71 | void Menu::() |
| 72 | { |
| 73 | delete errorMessage; |
| 74 | errorMessage = nullptr; |
| 75 | } |
| 76 | |
| 77 | void Menu::() |
| 78 | { |
| 79 | if (entries.empty()) |
| 80 | return; |
| 81 | entries[0]->unselect(); |
| 82 | sort(entries.begin(), entries.end(), [](Entry* a, Entry* b) { |
| 83 | return a->get_label() < b->get_label(); |
| 84 | }); |
| 85 | entries[0]->select(); |
| 86 | } |
| 87 | |
| 88 | void Menu::(u8 const* keys) |
| 89 | { |
| 90 | int oldCursor = cursor; |
| 91 | |
| 92 | if (keys[SDL_SCANCODE_DOWN] and cursor < entries.size() - 1) |
| 93 | { |
| 94 | cursor++; |
| 95 | if (cursor == bottom) { |
| 96 | bottom += 1; |
| 97 | top += 1; |
| 98 | } |
| 99 | clear_error(); |
| 100 | } |
| 101 | else if (keys[SDL_SCANCODE_UP] and cursor > 0) |
| 102 | { |
| 103 | cursor--; |
| 104 | if (cursor < top) { |
| 105 | top -= 1; |
| 106 | bottom -= 1; |
| 107 | } |
| 108 | clear_error(); |
| 109 | } |
| 110 | entries[oldCursor]->unselect(); |
| 111 | entries[cursor]->select(); |
| 112 | |
| 113 | if (keys[SDL_SCANCODE_RETURN]) |
| 114 | entries[cursor]->trigger(); |
| 115 | } |
| 116 | |
| 117 | void Menu::() |
| 118 | { |
| 119 | for (int i = top; i < entries.size() && i < bottom; ++i) |
| 120 | { |
| 121 | int y = (i - top) * FONT_SZ; |
| 122 | entries[i]->render(TEXT_CENTER, y); |
| 123 | } |
| 124 | |
| 125 | if (errorMessage != nullptr) |
| 126 | errorMessage->render(TEXT_CENTER, HEIGHT - FONT_SZ * 3 / 2); |
| 127 | } |
| 128 | |
| 129 | void FileMenu::(string dir) |
| 130 | { |
| 131 | clear(); |
| 132 | |
| 133 | struct dirent* dirp; |
| 134 | DIR* dp = opendir(dir.c_str()); |
| 135 | |
| 136 | while ((dirp = readdir(dp)) != NULL) |
| 137 | { |
| 138 | string name = dirp->d_name; |
| 139 | string path = dir + "/" + name; |
| 140 | |
| 141 | if (name[0] == '.' and name != ".." ) continue; |
| 142 | |
| 143 | if (dirp->d_type == DT_DIR) |
| 144 | add(new Entry(name + "/" , |
| 145 | [=]{ change_dir(path); })); |
| 146 | |
| 147 | else if (name.size() > 4 and name.substr(name.size() - 4) == ".nes" ) |
| 148 | add(new Entry(name, |
| 149 | [=]{ load_rom(path); })); |
| 150 | } |
| 151 | closedir(dp); |
| 152 | sort_by_label(); |
| 153 | } |
| 154 | |
| 155 | void FileMenu::(string path) |
| 156 | { |
| 157 | clear_error(); |
| 158 | Cartridge::load(path.c_str()); |
| 159 | if (!Cartridge::loaded()) |
| 160 | { |
| 161 | errorMessage = new Entry("Load failed" ); |
| 162 | return errorMessage->select(); |
| 163 | } |
| 164 | toggle_pause(); |
| 165 | } |
| 166 | |
| 167 | FileMenu::() |
| 168 | { |
| 169 | char cwd[512]; |
| 170 | change_dir(getcwd(cwd, 512)); |
| 171 | } |
| 172 | |
| 173 | |
| 174 | } |
| 175 | |