1#include <algorithm>
2#include <dirent.h>
3#include <unistd.h>
4#include "cartridge.hpp"
5#include "menu.hpp"
6
7namespace GUI {
8
9using namespace std;
10
11
12Entry::Entry(string label, function<void()> callback) : callback(callback)
13{
14 set_label(label);
15}
16
17Entry::~Entry()
18{
19 SDL_DestroyTexture(whiteTexture);
20 SDL_DestroyTexture(redTexture);
21}
22
23void 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
34void Entry::render(int x, int y) {
35 render_texture(selected ? redTexture : whiteTexture, x, y);
36}
37
38ControlEntry::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
46ControlEntry::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
55void Menu::add(Entry* entry)
56{
57 if (entries.empty())
58 entry->select();
59 entries.push_back(entry);
60}
61
62void Menu::clear()
63{
64 for (auto entry : entries)
65 delete entry;
66 entries.clear();
67 clear_error();
68 cursor = 0;
69}
70
71void Menu::clear_error()
72{
73 delete errorMessage;
74 errorMessage = nullptr;
75}
76
77void Menu::sort_by_label()
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
88void Menu::update(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
117void Menu::render()
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
129void FileMenu::change_dir(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
155void FileMenu::load_rom(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
167FileMenu::FileMenu()
168{
169 char cwd[512];
170 change_dir(getcwd(cwd, 512));
171}
172
173
174}
175